test_emails.py 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import re
  2. from urllib.parse import urlencode
  3. from selenium.webdriver.common.by import By
  4. from sentry.receivers import create_default_projects
  5. from sentry.testutils.cases import AcceptanceTestCase
  6. from sentry.testutils.factories import get_fixture_path
  7. from sentry.testutils.silo import no_silo_test
  8. EMAILS = (
  9. ("/debug/mail/assigned/", "assigned"),
  10. ("/debug/mail/assigned/self/", "assigned self"),
  11. ("/debug/mail/note/", "note"),
  12. ("/debug/mail/regression/", "regression"),
  13. ("/debug/mail/regression/release/", "regression with version"),
  14. ("/debug/mail/new-release/", "release"),
  15. ("/debug/mail/resolved/", "resolved"),
  16. ("/debug/mail/resolved-in-release/", "resolved in release"),
  17. ("/debug/mail/resolved-in-release/upcoming/", "resolved in release upcoming"),
  18. ("/debug/mail/unassigned/", "unassigned"),
  19. ("/debug/mail/unable-to-fetch-commits/", "unable to fetch commits"),
  20. ("/debug/mail/unable-to-delete-repo/", "unable to delete repo"),
  21. ("/debug/mail/error-alert/", "alert"),
  22. ("/debug/mail/performance-alert/transaction-n-plus-one", "performance"),
  23. ("/debug/mail/performance-alert/transaction-n-plus-one-api-call/", "n1 api call"),
  24. ("/debug/mail/digest/", "digest"),
  25. ("/debug/mail/invalid-identity/", "invalid identity"),
  26. ("/debug/mail/invitation/", "invitation"),
  27. ("/debug/mail/mfa-added/", "mfa added"),
  28. ("/debug/mail/mfa-removed/", "mfa removed"),
  29. ("/debug/mail/recovery-codes-regenerated/", "recovery codes regenerated"),
  30. ("/debug/mail/password-changed/", "password changed"),
  31. ("/debug/mail/sso-linked", "sso linked"),
  32. ("/debug/mail/sso-unlinked", "sso unlinked"),
  33. ("/debug/mail/sso-unlinked/no-password", "sso unlinked without password"),
  34. )
  35. def read_txt_email_fixture(name: str) -> str:
  36. # "sso unlinked without password"
  37. # => "sso_unlinked_without_password.txt"
  38. filename = name.replace(" ", "_") + ".txt"
  39. with open(get_fixture_path("emails", filename)) as f:
  40. return f.read()
  41. def build_url(path: str, format: str = "html") -> str:
  42. return f"{path}?{urlencode({'format': format, 'seed': b'123', 'is_test': True})}"
  43. def redact_ids(text: str) -> str:
  44. issues_re = re.compile("(testserver/organizations/sentry/issues/[0-9]+/)")
  45. match = issues_re.search(text)
  46. if match:
  47. for g in match.groups():
  48. text = text.replace(g, "testserver/organizations/sentry/issues/x/")
  49. return text
  50. def redact_notification_uuid(text: str) -> str:
  51. return re.sub("uuid=[A-Za-z0-9_-]+", "uuid=x", text)
  52. def replace_amp(text: str) -> str:
  53. return re.sub("¬", "&not", text)
  54. @no_silo_test(stable=True)
  55. class EmailTestCase(AcceptanceTestCase):
  56. def setUp(self):
  57. super().setUp()
  58. create_default_projects()
  59. # This email address is required to match FIXTURES.
  60. self.user = self.create_user("foo@example.com")
  61. self.login_as(self.user)
  62. def test_emails(self):
  63. for url, name in EMAILS:
  64. # HTML output is captured as a snapshot
  65. self.browser.get(build_url(url, "html"))
  66. self.browser.wait_until("#preview")
  67. # Text output is asserted against static fixture files
  68. self.browser.get(build_url(url, "txt"))
  69. self.browser.wait_until("#preview")
  70. elem = self.browser.find_element(by=By.CSS_SELECTOR, value="#preview pre")
  71. text_src = elem.get_attribute("innerHTML")
  72. # Avoid relying on IDs as this can cause flakey tests
  73. text_src = redact_ids(replace_amp(text_src))
  74. fixture_src = read_txt_email_fixture(name)
  75. assert redact_notification_uuid(fixture_src) == redact_notification_uuid(text_src)