Skip to content

feat(editor): send beehiiv test emails from the post editor - #190

Merged
phpbits merged 10 commits into
release/1.1.0from
feature/19812449-send-test-email
Oct 16, 2026
Merged

phpbits merged 10 commits into
release/1.1.0from
feature/19812449-send-test-email

Conversation

@phpbits

@phpbits phpbits commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a "Send test email" section to the beehiiv panel in the post editor. Publishers can send a test email of the saved post to addresses they type in, without sending the real newsletter. It shows the daily send limit and gives clear messages when a send fails.

Depends on the E2E/PHPUnit setup PR (#). Review that first, or review only the last 4 commits here.

Changes

Editor (editor-integration/newsletter-preview/test-email-send)

  • New test email section in the panel (test-email-send.js). You type recipient addresses, the post is saved first, and the controls are disabled while it saves or sends.
  • It shows how many sends are left and when the limit resets. The reset time is remembered once the daily limit is hit.
  • On failure, the addresses you typed are kept.
  • The panel shows a note if a leftover draft remains after a failure.
  • Available for draft, pending and scheduled posts only, and only for users with publish and edit rights.

Server (TestSender, TestSendController)

  • New REST endpoint and TestSender class.
  • A test send uses the post's linked unsent newsletter if there is one. Otherwise it creates a temporary draft, which is always a draft and is always deleted afterwards.
  • The post's own newsletter state is not touched.
  • The server refuses a test send when no default post template is set, the same readiness rule as the newsletter toggle. This also applies on the linked-newsletter path, which used to skip the check.
  • For request failures, beehiiv's own error message is shown instead of the real send's generic "Try saving the post again" text. Auth (401/403) and rate-limit (429) errors keep their own messages.
  • Changes in Posts.php and Sender.php support the above.

Tests

  • New NewsletterTestSenderTest (PHPUnit). It asserts the full beehiiv message for 400 and 5xx, the reconnect message for 403, and the template refusal on the linked path.
  • New test-email-send.spec.js (E2E), one test per acceptance criterion (AC-001 to AC-024).
  • The test-only mu-plugin gains regex and method-aware HTTP mocks, an optional response delay, a request log, and a forced settings-status option.
  • Fixes a race in the shared loginAs helper. wp-login.php refocuses the username field on a timer, so the password could be typed into the username field. The helper now re-fills until both fields hold the expected values.
  • Regenerates languages/beehiiv.pot.

How to test

  1. Open a draft post, find "Send test email" in the beehiiv panel, enter an address and send. The post should save first and you should get a success message with the sends left.
  2. Check that no real newsletter was sent or created. A linked post's newsletter state and a temporary draft should both be untouched or deleted.
  3. Remove the default post template and try again. The test send should be refused before any beehiiv call.
  4. Trigger a beehiiv error. You should see beehiiv's own message, and the addresses should stay in the field.
  5. Hit the daily limit. The section should show when it resets.
  6. Check that published posts and users without publish rights don't see or can't use the section.
npm run test:e2e
npx wp-env run tests-cli -- bash -c "cd wp-content/plugins/beehiiv && composer test"

Feature: editor-integration/newsletter-preview/test-email-send

Implements:
- US-001: Send a test email of my saved post
- US-002: Know when test send is available
- US-003: See the daily limit and understand failures

Verified:
- AC-001 to AC-006: test email section in the beehiiv panel, typed recipients, save-first, disabled while saving or sending ✓
- AC-007 to AC-011: linked unsent newsletter or a temporary draft that is always a draft and always deleted; post newsletter state untouched ✓
- AC-012 to AC-017: draft, pending, and scheduled posts only; publish and edit rights; readiness and sync checks enforced on the server ✓
- AC-018 to AC-024: sends left and reset time, remembered reset time on the daily limit, rate-limit and generic errors, addresses kept, leftover-draft note ✓
… send

Playwright spec for PRD-06.5.01 Test Email Send, one test per acceptance
criterion. 22 of 24 ACs pass. AC-017 (server skips the default template
check on the linked-newsletter path) and AC-022 (beehiiv's own error
message is replaced with generic copy for 400/404/5xx) fail and are not
fixed here.

The test-only mu-plugin gains regex and method-aware HTTP mocks, an
optional response delay, a request log, and a forced settings-status
option for the draft-forcing check. Existing specs keep their behaviour.
…s error text for test emails

- Refuse a test send on the server when no default post template is set,
  the same readiness rule as the newsletter toggle, before any beehiiv
  call. The linked-newsletter path skipped this check (AC-015, AC-017).
- Show beehiiv's own error message for request failures instead of the
  real send's generic "Try saving the post again" copy. Auth (401/403)
  and rate-limit (429) errors keep their own messages (AC-022, BR-007).
- Strengthen the PHPUnit coverage: assert the full beehiiv message for
  400 and 5xx, the reconnect message for 403, and the template refusal
  on the linked path.
wp-login.php focuses and selects the username field on a timer after
load. When it fired between the two fills, the password was typed into
the username field, the empty password blocked the submit, and the
waitForURL timed out (seen in oauth-authorization AC-005's beforeEach).
Re-fill until both fields hold the expected values before submitting.
@phpbits phpbits mentioned this pull request Oct 13, 2026
5 tasks
…ft before a test send

beehiiv creates Send API posts in the background: the create call returns
the new post's id straight away, but the post doesn't exist until creation
finishes. Test sends to an unlinked draft called test_sends immediately and
failed with "Couldn't find post with 'id'=post_…" about two times in three
(staging report on Teamwork task 19812449).

- Poll the new draft until beehiiv reports it created (200), honoring
  Retry-After, for up to 20 seconds; report a clear message when creation
  fails (404) or doesn't finish in time. Retry a not-found test send once.
  The temporary draft is still always deleted.
- Add Posts::get_creation_state() and keep the Retry-After header in Client.
- PHPUnit: cover still-creating-then-ready, failed creation, the wait limit,
  and the not-found retry. E2E: mock the creation check and expect it in
  the request sequence.

PRD-06.5.01 AC-025 (Q-004 answered).
Addresses the review of 383d7a8.

- Retry deleting a temporary draft beehiiv is still processing (202),
  treat a 404 as already gone, and report a leftover draft on failure
  responses too; the editor shows the leftover note on errors (AC-024,
  AC-025, R-003).
- Use one bounded delay for the deadline check and the pause; clamp the
  wait, poll and cleanup filters to finite ranges.
- Keep waiting on 429, 5xx and transport errors from the creation check;
  cap each check's HTTP timeout to the time left (R-004).
- Parse Retry-After given as an HTTP date.
- Fall back to the remembered reset time when beehiiv omits it (AC-018).
- Check eligibility in the same order in the editor as on the server,
  and sanitize the recipients REST argument.
- Tests: Retry-After honored, a real non-zero wait budget, exactly two
  test sends on the not-found retry, cleanup retries, leftover reporting
  and the reset fallback (PHPUnit); AC-025 and R-003 E2E tests, with
  response sequences, headers and a wait override in the mock plugin.
Conflicts resolved:
- tests/e2e/utils/auth.js: both branches fixed the wp-login focus race.
  Wait for the page's own focus first (release/1.1.0), then fill with
  the verify-and-retry loop.
- tests/e2e/plugins/beehiiv-options.php: keep release/1.1.0's request
  log, per-mock needle and WordPress.org blocking; add back the regex
  pattern, delay, response sequence and headers, the answering mock's
  key in the log, and the test-send filters the test-email spec uses.
- languages/beehiiv.pot: regenerated from the merged code.

Integration fixes for the merged features:
- Test sends use the post's resolved publication (per-post publication,
  #191) for the temporary draft, the linked newsletter and the payload,
  not the site-wide default.
- A newsletter the sync flow has recorded as sent (#192) can't be
  tested, even while its stored schedule is still in the future.
- PHPUnit covers both.
@phpbits
phpbits merged commit d1c4ff5 into release/1.1.0 Oct 16, 2026
19 of 20 checks passed
@phpbits
phpbits deleted the feature/19812449-send-test-email branch October 16, 2026 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant