Skip to content

Commit

Permalink
Send test email (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
mgax authored Jun 17, 2024
1 parent cf7a4c4 commit 45f4b4a
Show file tree
Hide file tree
Showing 11 changed files with 225 additions and 8 deletions.
16 changes: 16 additions & 0 deletions tests/campaign_backends/test_mailchimp_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
CAMPAIGN_ID = "test-campaign-id"
NEW_CAMPAIGN_ID = "test-new-campaign-id"
API_ERROR_TEXT = "something failed"
EMAIL = "[email protected]"


class MockMailchimpCampaignBackend(MailchimpCampaignBackend):
Expand Down Expand Up @@ -349,3 +350,18 @@ def get_campaign_request_body(self, *, recipients, subject):
"type": "regular",
}
assert backend.client.campaigns.create.mock_calls == [call(expected_body)]


def test_send_test_email(backend: MockMailchimpCampaignBackend):
backend.send_test_email(campaign_id=CAMPAIGN_ID, email=EMAIL)
assert backend.client.campaigns.send_test_email.mock_calls == [
call(CAMPAIGN_ID, {"test_emails": [EMAIL], "send_type": "html"})
]


def test_send_test_email_failure(backend: MockMailchimpCampaignBackend):
backend.client.campaigns.send_test_email.side_effect = ApiClientError("", 400)
with pytest.raises(CampaignBackendError) as error:
backend.send_test_email(campaign_id=CAMPAIGN_ID, email=EMAIL)

assert error.match("Error while sending test email")
3 changes: 3 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ def save_campaign(self, **kwargs):
def get_campaign(self, campaign_id: str):
raise NotImplementedError

def send_test_email(self, *, campaign_id: str, email: str):
raise NotImplementedError


@pytest.fixture(autouse=True)
def memory_backend(monkeypatch: pytest.MonkeyPatch):
Expand Down
60 changes: 58 additions & 2 deletions tests/test_campaign_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

CAMPAIGN_ID = "test-campaign-id"
CAMPAIGN_URL = "http://campaign.example.com"
EMAIL = "[email protected]"


@pytest.fixture
Expand All @@ -34,7 +35,7 @@ def test_save_campaign(
data = {
"title": page.title,
"slug": page.slug,
"newsletter_action": "save_campaign",
"newsletter-action": "save_campaign",
}
response = admin_client.post(url, data, follow=True)

Expand Down Expand Up @@ -63,11 +64,66 @@ def test_save_campaign_failed_to_save(
data = {
"title": page.title,
"slug": page.slug,
"newsletter_action": "save_campaign",
"newsletter-action": "save_campaign",
}
response = admin_client.post(url, data, follow=True)

assert "Failed to save newsletter campaign" in response.content.decode()

page.refresh_from_db()
assert page.newsletter_campaign == ""


def test_send_test_email(
page: ArticlePage, admin_client: Client, memory_backend: MemoryCampaignBackend
):
memory_backend.save_campaign = Mock(return_value=CAMPAIGN_ID)
memory_backend.get_campaign = Mock(return_value=Mock(url=CAMPAIGN_URL))
memory_backend.send_test_email = Mock()

url = reverse("wagtailadmin_pages:edit", kwargs={"page_id": page.pk})
data = {
"title": page.title,
"slug": page.slug,
"newsletter-action": "send_test_email",
"newsletter-test-email": EMAIL,
}
response = admin_client.post(url, data, follow=True)

html = response.content.decode()
assert f"Page '{page.title}' has been updated" in html
assert (
f"Newsletter campaign '{page.title}' has been saved to Testing"
in html
)
assert f"Test message sent to '{EMAIL}'" in html

assert memory_backend.save_campaign.mock_calls == [
call(campaign_id="", recipients=None, subject=page.title, html=ANY)
]
assert memory_backend.send_test_email.mock_calls == [
call(campaign_id=CAMPAIGN_ID, email=EMAIL)
]


def test_send_test_email_invalid_email(
page: ArticlePage, admin_client: Client, memory_backend: MemoryCampaignBackend
):
memory_backend.save_campaign = Mock(return_value=CAMPAIGN_ID)
memory_backend.send_test_email = Mock()

url = reverse("wagtailadmin_pages:edit", kwargs={"page_id": page.pk})
data = {
"title": page.title,
"slug": page.slug,
"newsletter-action": "send_test_email",
"newsletter-test-email": "invalid-address",
}
response = admin_client.post(url, data, follow=True)

html = response.content.decode()
assert f"Page '{page.title}' has been updated" in html
assert "'email': Enter a valid email address." in html

assert memory_backend.save_campaign.mock_calls == []
assert memory_backend.send_test_email.mock_calls == []
22 changes: 22 additions & 0 deletions wagtail_newsletter/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from wagtail.admin import messages

from wagtail_newsletter.forms import SendTestEmailForm

from . import campaign_backends
from .models import NewsletterPageMixin

Expand Down Expand Up @@ -29,3 +31,23 @@ def save_campaign(request, page: NewsletterPageMixin) -> None:
messages.success(
request, f"Newsletter campaign {subject!r} has been saved to {backend.name}"
)


def send_test_email(request, page: NewsletterPageMixin):
form = SendTestEmailForm(request.POST, prefix="newsletter-test")
if not form.is_valid():
for field, errors in form.errors.items():
for message in errors:
messages.error(request, f"{field!r}: {message}")
return

email = form.cleaned_data["email"]

save_campaign(request, page)

backend = campaign_backends.get_backend()
backend.send_test_email(
campaign_id=page.newsletter_campaign,
email=email,
)
messages.success(request, f"Test message sent to {email!r}")
3 changes: 3 additions & 0 deletions wagtail_newsletter/campaign_backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ def save_campaign(
@abstractmethod
def get_campaign(self, campaign_id: str) -> Optional[Campaign]: ...

@abstractmethod
def send_test_email(self, *, campaign_id: str, email: str) -> None: ...


def get_backend() -> CampaignBackend:
backend_class = import_string(
Expand Down
15 changes: 15 additions & 0 deletions wagtail_newsletter/campaign_backends/mailchimp.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,21 @@ def get_campaign(self, campaign_id: str) -> Optional[MailchimpCampaign]:
status=data["status"],
)

def send_test_email(self, *, campaign_id: str, email: str) -> None:
try:
self.client.campaigns.send_test_email(
campaign_id,
{
"test_emails": [email],
"send_type": "html",
},
)

except ApiClientError as error:
_log_and_raise(
error, "Error while sending test email", campaign_id=campaign_id
)


def _log_and_raise(error: ApiClientError, message: str, **kwargs) -> NoReturn:
kwargs["status_code"] = error.status_code
Expand Down
8 changes: 8 additions & 0 deletions wagtail_newsletter/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django import forms


class SendTestEmailForm(forms.Form):
email = forms.EmailField(
label="Email address",
help_text="Send a test email to this address",
)
10 changes: 10 additions & 0 deletions wagtail_newsletter/panels.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from django.utils.html import format_html
from wagtail.admin.panels import Panel

from wagtail_newsletter.forms import SendTestEmailForm

from . import campaign_backends, models


Expand Down Expand Up @@ -51,4 +53,12 @@ def get_context_data(self, parent_context=None):

context["backend_name"] = backend.name
context["campaign"] = campaign
context["test_form"] = SendTestEmailForm(
initial={"email": self.request.user.email},
)
return context

class Media:
js = [
"wagtail_newsletter/js/wagtail_newsletter.js",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
window.wagtail.app.register("wn-panel",
class extends window.StimulusModule.Controller {
static targets = [
"testButton",
"testAddress",
"testSubmit",
]

get testButtonProgress() {
return this.application.getControllerForElementAndIdentifier(
this.testButtonTarget, "w-progress"
);
}

test(event) {
this.testAddressTarget.value = event.detail.address;
this.testButtonProgress.activate();
this.testSubmitTarget.click();
}
}
);


window.wagtail.app.register("wn-test",
class extends window.StimulusModule.Controller {
get dialog() {
return this.application.getControllerForElementAndIdentifier(
this.element.closest("[data-controller=w-dialog]"), "w-dialog"
);
}

submit() {
const address = this.element.querySelector("input[name=email]").value;
this.dispatch("submit", { detail: { address } });
this.dialog.hide();
}
}
);
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{% load wagtailadmin_tags %}

<div class="wn-panel">
<div
class="wn-panel"
data-controller="wn-panel"
data-action="wn-test:submit@window->wn-panel#test"
>
{% if error_message %}
<div class="help-block help-critical">
{% icon name="warning" %}
Expand All @@ -10,21 +14,60 @@

<div class="help-block help-info">
{% icon name="help" %}
This action will save a new page revision.
These actions will save a new page revision.
</div>

<p>
<input
type="hidden"
name="newsletter-test-email"
data-wn-panel-target="testAddress"
>

<button
type="submit"
class="button button-secondary button-longrunning"
name="newsletter_action"
name="newsletter-action"
value="save_campaign"
data-controller="w-progress"
data-action="w-progress#activate"
>
{% icon name="spinner" %}
Save campaign to {{ backend_name }}
</button>

<button
type="button"
class="button button-secondary button-longrunning"
data-a11y-dialog-show="wn-test-dialog"
data-controller="w-progress"
data-wn-panel-target="testButton"
>
{% icon name="spinner" %}
Send test email
</button>

{% dialog icon_name="mail" id="wn-test-dialog" title="Test email" %}
<form
data-controller="wn-test"
data-action="wn-test#submit:prevent"
>
{% include "wagtailadmin/shared/field.html" with field=test_form.email %}

<button type="submit" class="button button-primary">Send</button>
<button type="button" class="button button-secondary" data-action="w-dialog#hide">Cancel</button>
</form>
{% enddialog %}

<button
type="submit"
class="w-hidden"
name="newsletter-action"
value="send_test_email"
data-wn-panel-target="testSubmit"
>
Send test email
</button>
</p>

{% if campaign %}
Expand Down
9 changes: 6 additions & 3 deletions wagtail_newsletter/wagtail_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,15 @@ def register_admin_viewset():
@hooks.register("after_create_page") # type: ignore
@hooks.register("after_edit_page") # type: ignore
def redirect_to_campaign_page(request, page: Page):
newsletter_action = request.POST.get("newsletter_action")
action = request.POST.get("newsletter-action")

if newsletter_action is None: # pragma: no cover
if action is None: # pragma: no cover
return

page = cast(NewsletterPageMixin, page)

if newsletter_action == "save_campaign":
if action == "save_campaign":
actions.save_campaign(request, page)

if action == "send_test_email":
actions.send_test_email(request, page)

0 comments on commit 45f4b4a

Please sign in to comment.