Add assertions for the templates in #2355 (Part 2 of 2) (#2356)

* Add assertions for the templates in #2355

- Move the TESTDATA functionality to a testing_config to be a test helper.
In this new helper, users can still retreive from it like a dictionary.
In addition, there's a helper function to help make goldens that can be
used to regenerate the golden templates. Add commented out
code to use it.
- Add some clean ups to individual tests or test cases. This was needed
because some of the existing rendering tests would pull from the database.
But since they only asserted for the existence of some values, it did
not matter. In the future, we can add mocks for all of those.
  - These cleanups also help for more than the rendering tests. Before these fixes, there were ~15 non-rendering tests that would fail if a developer ran the full suite with a clean db, then re-ran a test indivdually without resetting the database. There are still 2 non-rendering tests that fail without clearing the database first. But this first iteartion got a lot of them.
- Add a missing test for the PrepublicationHandler to increase coverage

* address comments

* more cleanup
This commit is contained in:
James C Scott III 2022-10-19 10:14:52 -04:00 коммит произвёл GitHub
Родитель dca47ebe61
Коммит 385d62374b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 230 добавлений и 40 удалений

Просмотреть файл

@ -88,6 +88,9 @@ class AccountsAPITest(testing_config.CustomTestCase):
self.assertTrue(new_appuser.is_site_editor)
self.assertFalse(new_appuser.is_admin)
# Clean up
new_appuser.key.delete()
def test_create__admin_valid(self):
"""Admin wants to register a new admin account."""
testing_config.sign_in('admin@example.com', 123567890)
@ -106,6 +109,9 @@ class AccountsAPITest(testing_config.CustomTestCase):
self.assertEqual('new_admin@example.com', new_appuser.email)
self.assertTrue(new_appuser.is_admin)
# Clean up
new_appuser.key.delete()
def test_create__forbidden(self):
"""Regular user cannot create an account."""
testing_config.sign_in('one@example.com', 123567890)

Просмотреть файл

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import datetime
import unittest
import testing_config # Must be imported before the module under test.
@ -20,6 +21,8 @@ import werkzeug.exceptions # Flask HTTP stuff.
from framework import utils
# Load testdata to be used across all of the test cases
TESTDATA = testing_config.Testdata(__file__)
class MockHandler(object):
@ -105,8 +108,11 @@ class UtilsFunctionTests(unittest.TestCase):
self.assertIsNone(handlerInstance.handler_called_with)
self.assertEqual('/request/path', handlerInstance.redirected_to)
def test_render_atom_feed__empty(self):
# For empty feeds, django sets the updated date to utcnow().
@mock.patch('django.utils.feedgenerator.SyndicationFeed.latest_post_date')
def test_render_atom_feed__empty(self, mock_latest_post_date):
"""It can render a feed with zero items."""
mock_latest_post_date.return_value = datetime.datetime(2017, 10, 7)
request = testing_config.Blank(
scheme='https', host='host',
path='/samples.xml')
@ -122,9 +128,9 @@ class UtilsFunctionTests(unittest.TestCase):
'max-age=63072000; includeSubDomains; preload',
'Content-Type': 'application/atom+xml;charset=utf-8',
})
self.assertIn('http://www.w3.org/2005/Atom', actual_text)
self.assertIn('<title>Local testing - test feed</title>', actual_text)
self.assertIn('<id>https://host/samples</id>', actual_text)
self.maxDiff = None
# TESTDATA.make_golden(actual_text, 'test_render_atom_feed__empty.html')
self.assertMultiLineEqual(TESTDATA['test_render_atom_feed__empty.html'], actual_text)
def test_render_atom_feed__some(self):
"""It can render a feed with some items."""
@ -148,16 +154,9 @@ class UtilsFunctionTests(unittest.TestCase):
actual_text, actual_headers = utils.render_atom_feed(
request, 'test feed', data)
self.assertIn('<title>feature one</title>', actual_text)
self.assertIn('one for testing</summary>', actual_text)
self.assertIn('/feature/12345678/</id>', actual_text)
self.assertIn('<category term="CSS"></category>', actual_text)
self.assertIn('<title>feature two</title>', actual_text)
self.assertIn('two for testing</summary>', actual_text)
self.assertIn('/feature/23456789/</id>', actual_text)
self.assertIn('<category term="Device"></category>', actual_text)
self.maxDiff = None
# TESTDATA.make_golden(actual_text, 'test_render_atom_feed__some.html')
self.assertMultiLineEqual(TESTDATA['test_render_atom_feed__some.html'], actual_text)
def test_get_banner_time__None(self):
"""If no time specified, it returns None."""

Просмотреть файл

@ -14,10 +14,8 @@
import collections
import json
import os
import testing_config # Must be imported before the module under test.
from datetime import datetime
from pathlib import Path
import flask
from unittest import mock
@ -37,15 +35,7 @@ test_app = flask.Flask(__name__,
template_folder=settings.flask_compat_get_template_path())
# Load testdata to be used across all of the CustomTestCases
TESTDATA = {}
testdata_dir = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
'testdata',
Path(__file__).stem
)
for filename in os.listdir(testdata_dir):
with open(os.path.join(testdata_dir, filename), 'r') as f:
TESTDATA[filename] = f.read()
TESTDATA = testing_config.Testdata(__file__)
class EmailFormattingTest(testing_config.CustomTestCase):
@ -96,7 +86,12 @@ class EmailFormattingTest(testing_config.CustomTestCase):
self.template_feature.key = ndb.Key('Feature', 123)
self.template_feature.put()
self.maxDiff = None
def tearDown(self):
self.watcher_1.key.delete()
self.component_owner_1.key.delete()
self.component_1.key.delete()
self.feature_1.key.delete()
self.feature_2.key.delete()
self.template_feature.key.delete()
@ -106,6 +101,7 @@ class EmailFormattingTest(testing_config.CustomTestCase):
with test_app.app_context():
body_html = notifier.format_email_body(
False, self.template_feature, [])
# TESTDATA.make_golden(body_html, 'test_format_email_body__new.html')
self.assertEqual(body_html,
TESTDATA['test_format_email_body__new.html'])
@ -114,6 +110,7 @@ class EmailFormattingTest(testing_config.CustomTestCase):
with test_app.app_context():
body_html = notifier.format_email_body(
True, self.template_feature, [])
# TESTDATA.make_golden(body_html, 'test_format_email_body__update_no_changes.html')
self.assertEqual(body_html,
TESTDATA['test_format_email_body__update_no_changes.html'])
@ -122,6 +119,7 @@ class EmailFormattingTest(testing_config.CustomTestCase):
with test_app.app_context():
body_html = notifier.format_email_body(
True, self.template_feature, self.changes)
# TESTDATA.make_golden(body_html, 'test_format_email_body__update_with_changes.html')
self.assertEqual(body_html,
TESTDATA['test_format_email_body__update_with_changes.html'])
@ -131,6 +129,7 @@ class EmailFormattingTest(testing_config.CustomTestCase):
with test_app.app_context():
body_html = notifier.format_email_body(
True, self.template_feature, self.changes)
# TESTDATA.make_golden(body_html, 'test_format_email_body__mozdev_links_mozilla.html')
self.assertEqual(body_html,
TESTDATA['test_format_email_body__mozdev_links_mozilla.html'])
@ -139,6 +138,7 @@ class EmailFormattingTest(testing_config.CustomTestCase):
with test_app.app_context():
body_html = notifier.format_email_body(
True, self.template_feature, self.changes)
# TESTDATA.make_golden(body_html, 'test_format_email_body__mozdev_links_non_mozilla.html')
self.assertEqual(body_html,
TESTDATA['test_format_email_body__mozdev_links_non_mozilla.html'])
@ -496,10 +496,11 @@ class FeatureStarTest(testing_config.CustomTestCase):
notifier.FeatureStar.set_star(email, feature_2_id)
actual = notifier.FeatureStar.get_user_stars(email)
self.assertEqual(
sorted([feature_1_id, feature_2_id, feature_3_id],
reverse=True),
actual)
expected_ids = [feature_1_id, feature_2_id, feature_3_id]
self.assertEqual(sorted(expected_ids, reverse=True), actual)
# Cleanup
for feature_id in expected_ids:
notifier.FeatureStar.get_star(email, feature_id).key.delete()
def test_get_feature_starrers__no_stars(self):
"""No user has starred the given feature."""

Просмотреть файл

@ -19,6 +19,11 @@ from unittest import mock
from internals import core_models
from internals import reminders
from google.cloud import ndb
# Load testdata to be used across all of the CustomTestCases
TESTDATA = testing_config.Testdata(__file__)
class MockResponse:
"""Creates a fake response object for testing."""
@ -46,19 +51,20 @@ def make_test_features():
class FunctionTest(testing_config.CustomTestCase):
def setUp(self):
self.feature_1, self.feature_2, self.feature_3 = make_test_features()
self.current_milestone_info = {
'earliest_beta': '2022-09-21T12:34:56',
}
self.feature_template = core_models.Feature(
name='feature one', summary='sum', owner=['feature_owner@example.com'],
category=1, ot_milestone_desktop_start=100)
self.feature_template.key = ndb.Key('Feature', 123)
# This test does not require saving to the database.
def tearDown(self):
self.feature_1.key.delete()
self.feature_2.key.delete()
self.feature_3.key.delete()
self.maxDiff = None
def test_build_email_tasks(self):
def test_build_email_tasks_feature_accuracy(self):
actual = reminders.build_email_tasks(
[(self.feature_1, 100)], '[Action requested] Update %s',
[(self.feature_template, 100)], '[Action requested] Update %s',
reminders.FeatureAccuracyHandler.EMAIL_TEMPLATE_PATH,
self.current_milestone_info)
self.assertEqual(1, len(actual))
@ -66,9 +72,23 @@ class FunctionTest(testing_config.CustomTestCase):
self.assertEqual('feature_owner@example.com', task['to'])
self.assertEqual('[Action requested] Update feature one', task['subject'])
self.assertEqual(None, task['reply_to'])
self.assertIn('/guide/verify_accuracy/%d' % self.feature_1.key.integer_id(),
task['html'])
# TESTDATA.make_golden(task['html'], 'test_build_email_tasks_feature_accuracy.html')
self.assertMultiLineEqual(
TESTDATA['test_build_email_tasks_feature_accuracy.html'], task['html'])
def test_build_email_tasks_prepublication(self):
actual = reminders.build_email_tasks(
[(self.feature_template, 100)], '[Action requested] Update %s',
reminders.PrepublicationHandler.EMAIL_TEMPLATE_PATH,
self.current_milestone_info)
self.assertEqual(1, len(actual))
task = actual[0]
self.assertEqual('feature_owner@example.com', task['to'])
self.assertEqual('[Action requested] Update feature one', task['subject'])
self.assertEqual(None, task['reply_to'])
# TESTDATA.make_golden(task['html'], 'test_build_email_tasks_prepublication.html')
self.assertMultiLineEqual(
TESTDATA['test_build_email_tasks_prepublication.html'], task['html'])
class FeatureAccuracyHandlerTest(testing_config.CustomTestCase):

Просмотреть файл

@ -217,6 +217,7 @@ class ActivityTest(testing_config.CustomTestCase):
name='feature a', summary='sum', owner=['feature_owner@example.com'],
category=1)
self.feature_1.put()
testing_config.sign_in('one@example.com', 123567890)
def tearDown(self):
feature_id = self.feature_1.key.integer_id()
@ -224,6 +225,7 @@ class ActivityTest(testing_config.CustomTestCase):
for activity in activities:
activity.key.delete()
self.feature_1.key.delete()
testing_config.sign_out()
def test_activities_created(self):
# stash_values is used to note what the original values of a feature are.

Просмотреть файл

@ -16,16 +16,18 @@ import testing_config # Must be imported before the module under test.
from unittest import mock
import os
import flask
import werkzeug
import html5lib
from google.cloud import ndb
from pages import blink_handler
from internals import user_models
test_app = flask.Flask(__name__)
# Load testdata to be used across all of the CustomTestCases
TESTDATA = testing_config.Testdata(__file__)
class BlinkTemplateTest(testing_config.CustomTestCase):
@ -41,16 +43,48 @@ class BlinkTemplateTest(testing_config.CustomTestCase):
self.app_admin.put()
testing_config.sign_in('admin@example.com', 123567890)
self.component_1 = user_models.BlinkComponent(name='Blink')
self.component_1.put()
self.component_2 = user_models.BlinkComponent(name='Blink>Accessibility')
self.component_2.put()
self.component_owner_1 = user_models.FeatureOwner(
name='owner_1', email='owner_1@example.com',
primary_blink_components=[self.component_1.key, self.component_2.key])
self.component_owner_1.key = ndb.Key('FeatureOwner', 111)
self.component_owner_1.put()
self.watcher_1 = user_models.FeatureOwner(
name='watcher_1', email='watcher_1@example.com',
watching_all_features=True)
self.watcher_1.key = ndb.Key('FeatureOwner', 222)
self.watcher_1.put()
with test_app.test_request_context(self.request_path):
self.template_data = self.handler.get_template_data()
self.template_data.update(self.handler.get_common_data())
self.template_data['nonce'] = 'fake nonce'
self.template_data['xsrf_token'] = ''
self.template_data['xsrf_token_expires'] = 0
self.full_template_path = self.handler.get_template_path(self.template_data)
self.maxDiff = None
def tearDown(self):
self.watcher_1.key.delete()
self.component_owner_1.key.delete()
self.component_1.key.delete()
self.component_2.key.delete()
testing_config.sign_out()
self.app_admin.key.delete()
def test_html_rendering(self):
"""We can render the template with valid html."""
template_text = self.handler.render(
self.template_data, self.full_template_path)
parser = html5lib.HTMLParser(strict=True)
document = parser.parse(template_text)
# TESTDATA.make_golden(template_text, 'BlinkTemplateTest_test_html_rendering.html')
self.assertMultiLineEqual(
TESTDATA['BlinkTemplateTest_test_html_rendering.html'], template_text)
class SubscribersTemplateTest(testing_config.CustomTestCase):
@ -58,6 +92,17 @@ class SubscribersTemplateTest(testing_config.CustomTestCase):
HANDLER_CLASS = blink_handler.SubscribersHandler
def setUp(self):
# need to patch in setup because the details are retreived here.
# unable to use method decorator for setUp.
self.mock_chrome_details_patch = mock.patch(
'pages.blink_handler.construct_chrome_channels_details')
mock_chrome_details = self.mock_chrome_details_patch.start()
mock_chrome_details.return_value = {
"stable": {"mstone": 1},
"beta": {"mstone": 2},
"dev": {"mstone": 3},
"canary": {"mstone": 4},
}
self.request_path = self.HANDLER_CLASS.TEMPLATE_PATH
self.handler = self.HANDLER_CLASS()
@ -68,13 +113,46 @@ class SubscribersTemplateTest(testing_config.CustomTestCase):
testing_config.sign_in('admin@example.com', 123567890)
self.component_1 = user_models.BlinkComponent(name='Blink')
self.component_1.put()
self.component_2 = user_models.BlinkComponent(name='Blink>Accessibility')
self.component_2.put()
self.component_owner_1 = user_models.FeatureOwner(
name='owner_1', email='owner_1@example.com',
primary_blink_components=[self.component_1.key, self.component_2.key])
self.component_owner_1.key = ndb.Key('FeatureOwner', 111)
self.component_owner_1.put()
self.watcher_1 = user_models.FeatureOwner(
name='watcher_1', email='watcher_1@example.com',
watching_all_features=True)
self.watcher_1.key = ndb.Key('FeatureOwner', 222)
self.watcher_1.put()
with test_app.test_request_context(self.request_path):
self.template_data = self.handler.get_template_data()
self.template_data.update(self.handler.get_common_data())
self.template_data['nonce'] = 'fake nonce'
self.template_data['xsrf_token'] = ''
self.template_data['xsrf_token_expires'] = 0
self.full_template_path = self.handler.get_template_path(self.template_data)
self.maxDiff = None
def tearDown(self):
self.mock_chrome_details_patch.stop()
self.watcher_1.key.delete()
self.component_owner_1.key.delete()
self.component_1.key.delete()
self.component_2.key.delete()
testing_config.sign_out()
self.app_admin.key.delete()
def test_html_rendering(self):
"""We can render the template with valid html."""
template_text = self.handler.render(
self.template_data, self.full_template_path)
parser = html5lib.HTMLParser(strict=True)
document = parser.parse(template_text)
# TESTDATA.make_golden(template_text, 'SubscribersTemplateTest_test_html_rendering.html')
self.assertMultiLineEqual(
TESTDATA['SubscribersTemplateTest_test_html_rendering.html'], template_text)

Просмотреть файл

@ -27,6 +27,8 @@ from framework import rediscache
test_app = flask.Flask(__name__)
# Load testdata to be used across all of the CustomTestCases
TESTDATA = testing_config.Testdata(__file__)
class TestWithFeature(testing_config.CustomTestCase):
@ -123,10 +125,18 @@ class FeatureListTemplateTest(TestWithFeature):
self.template_data = self.handler.get_template_data(
feature_id=self.feature_id)
testing_config.sign_in('admin@example.com', 111)
self.template_data.update(self.handler.get_common_data())
self.template_data['nonce'] = 'fake nonce'
self.template_data['xsrf_token'] = ''
self.template_data['xsrf_token_expires'] = 0
template_path = self.handler.get_template_path(self.template_data)
self.full_template_path = os.path.join(template_path)
self.maxDiff = None
def tearDown(self):
testing_config.sign_out()
def test_html_rendering(self):
"""We can render the template with valid html."""
@ -134,6 +144,9 @@ class FeatureListTemplateTest(TestWithFeature):
self.template_data, self.full_template_path)
parser = html5lib.HTMLParser(strict=True)
document = parser.parse(template_text)
# TESTDATA.make_golden(template_text, 'test_html_rendering.html')
self.assertMultiLineEqual(
TESTDATA['test_html_rendering.html'], template_text)
class FeatureListXMLHandlerTest(TestWithFeature):

Просмотреть файл

@ -21,12 +21,15 @@ import flask
import werkzeug
import html5lib
from google.cloud import ndb
from pages import intentpreview
from internals import core_enums
from internals import core_models
test_app = flask.Flask(__name__)
# Load testdata to be used across all of the CustomTestCases
TESTDATA = testing_config.Testdata(__file__)
class IntentEmailPreviewHandlerTest(testing_config.CustomTestCase):
@ -189,6 +192,8 @@ class IntentEmailPreviewTemplateTest(testing_config.CustomTestCase):
self.feature_1 = core_models.Feature(
name='feature one', summary='sum', owner=['user1@google.com'],
category=1, intent_stage=core_enums.INTENT_IMPLEMENT)
# Hardcode the key for the template test
self.feature_1.key = ndb.Key('Feature', 234)
self.feature_1.put()
self.request_path = '/admin/features/launch/%d/%d?intent' % (
@ -196,6 +201,7 @@ class IntentEmailPreviewTemplateTest(testing_config.CustomTestCase):
self.handler = self.HANDLER_CLASS()
self.feature_id = self.feature_1.key.integer_id()
testing_config.sign_in('user1@google.com', 123567890)
with test_app.test_request_context(self.request_path):
self.template_data = self.handler.get_template_data(
feature_id=self.feature_id)
@ -207,13 +213,26 @@ class IntentEmailPreviewTemplateTest(testing_config.CustomTestCase):
template_path = self.handler.get_template_path(self.template_data)
self.full_template_path = os.path.join(template_path)
self.maxDiff = None
def tearDown(self):
self.feature_1.key.delete()
testing_config.sign_out()
def test_html_rendering(self):
"""We can render the template with valid html."""
testing_config.sign_in('user1@google.com', 123567890)
with test_app.test_request_context(self.request_path):
actual_data = self.handler.get_template_data(feature_id=self.feature_id)
actual_data.update(self.handler.get_common_data())
actual_data['nonce'] = 'fake nonce'
actual_data['xsrf_token'] = ''
actual_data['xsrf_token_expires'] = 0
template_text = self.handler.render(
actual_data, self.full_template_path)
testing_config.sign_out()
parser = html5lib.HTMLParser(strict=True)
document = parser.parse(template_text)
# TESTDATA.make_golden(template_text, 'test_html_rendering.html')
self.assertMultiLineEqual(
TESTDATA['test_html_rendering.html'], template_text)

Просмотреть файл

@ -17,12 +17,18 @@ import testing_config # Must be imported first
import flask
import html5lib
from google.cloud import ndb
from unittest import mock
from internals import user_models
from pages import users
test_app = flask.Flask(__name__)
# Load testdata to be used across all of the CustomTestCases
TESTDATA = testing_config.Testdata(__file__)
class UsersListTemplateTest(testing_config.CustomTestCase):
def setUp(self):
@ -30,12 +36,22 @@ class UsersListTemplateTest(testing_config.CustomTestCase):
self.app_admin = user_models.AppUser(email='admin@example.com')
self.app_admin.is_admin = True
self.app_admin.key = ndb.Key('AppUser', 1)
self.app_admin.put()
testing_config.sign_in('admin@example.com', 123567890)
with test_app.test_request_context('/request_path'):
self.template_data = self.handler.get_template_data()
self.template_data.update(self.handler.get_common_data())
self.template_data['nonce'] = 'fake nonce'
self.template_data['xsrf_token'] = ''
self.template_data['xsrf_token_expires'] = 0
self.full_template_path = self.handler.get_template_path(self.template_data)
self.maxDiff = None
def tearDown(self):
self.app_admin.key.delete()
testing_config.sign_out()
def test_html_rendering(self):
"""We can render the template with valid html."""
@ -43,3 +59,6 @@ class UsersListTemplateTest(testing_config.CustomTestCase):
self.template_data, self.full_template_path)
parser = html5lib.HTMLParser(strict=True)
document = parser.parse(template_text)
# TESTDATA.make_golden(template_text, 'test_html_rendering.html')
self.assertMultiLineEqual(
TESTDATA['test_html_rendering.html'], template_text)

Просмотреть файл

@ -17,6 +17,7 @@ import os
import unittest
from google.cloud import ndb
from pathlib import Path
os.environ['DJANGO_SECRET'] = 'test secret'
os.environ['SERVER_SOFTWARE'] = 'test ' + os.environ.get('SERVER_SOFTWARE', '')
@ -89,3 +90,35 @@ class CustomTestCase(unittest.TestCase):
client = ndb.Client()
with client.context():
super(CustomTestCase, self).run(result=result)
class Testdata(object):
def __init__(self, test_file_path: str):
"""Helper class to load testdata
Common pattern to place the testdata in the following format:
Given a test file, atest_test.py, and it is located at
/some/module/atest_test.py.
The testdata should be located at /some/module/testdata/atest_test/
"""
self.testdata = {}
test_file_name = Path(test_file_path).stem
self.testdata_dir = os.path.join(
os.path.abspath(os.path.dirname(test_file_path)),
'testdata',
test_file_name)
for filename in os.listdir(self.testdata_dir):
test_data_file_path = os.path.join(self.testdata_dir, filename)
with open(test_data_file_path, 'r', encoding='UTF-8') as f:
self.testdata[filename] = f.read()
def make_golden(self, raw_data, test_data_file_name):
"""Helper function to make golden file
"""
test_data_file_path = os.path.join(self.testdata_dir, test_data_file_name)
with open(test_data_file_path, 'w', encoding='UTF-8') as f:
f.write(raw_data)
def __getitem__(self, key):
return self.testdata[key]