Skip to content

feat: allow mails to be intercepted using events #120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Feb 26, 2025
60 changes: 0 additions & 60 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -1092,12 +1092,6 @@ parameters:
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:createAndSetCsrfCookie\(\) should return array\<string\> but returns mixed\.$#'
identifier: return.type
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:dontSee\(\) has parameter \$selector with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
Expand Down Expand Up @@ -1194,12 +1188,6 @@ parameters:
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:getInternalDomains\(\) should return non\-empty\-list\<string\> but returns mixed\.$#'
identifier: return.type
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabAttributeFrom\(\) has parameter \$cssOrXpath with no type specified\.$#'
identifier: missingType.parameter
Expand All @@ -1212,48 +1200,18 @@ parameters:
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabFixture\(\) should return yii\\db\\ActiveRecord\|yii\\test\\Fixture\|null but returns mixed\.$#'
identifier: return.type
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabFixtures\(\) has invalid return type tests\\_generated\\Fixture\.$#'
identifier: class.notFound
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabFixtures\(\) should return array\<string, tests\\_generated\\Fixture\> but returns mixed\.$#'
identifier: return.type
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabLastSentEmail\(\) should return yii\\mail\\BaseMessage\|null but returns mixed\.$#'
identifier: return.type
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabMultiple\(\) has parameter \$cssOrXpath with no type specified\.$#'
identifier: missingType.parameter
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabMultiple\(\) should return array\<string\> but returns mixed\.$#'
identifier: return.type
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabPageSource\(\) should return string but returns mixed\.$#'
identifier: return.type
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabRecord\(\) has invalid return type tests\\_generated\\ActiveRecordInterface\.$#'
identifier: class.notFound
Expand All @@ -1266,30 +1224,12 @@ parameters:
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabRecord\(\) should return array\|yii\\db\\ActiveRecordInterface\|null but returns mixed\.$#'
identifier: return.type
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabSentEmails\(\) has invalid return type tests\\_generated\\BaseMessage\.$#'
identifier: class.notFound
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabSentEmails\(\) has invalid return type tests\\_generated\\MessageInterface\.$#'
identifier: class.notFound
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabSentEmails\(\) should return list\<tests\\_generated\\BaseMessage&tests\\_generated\\MessageInterface\> but returns mixed\.$#'
identifier: return.type
count: 1
path: tests/_support/FunctionalTester.php

-
message: '#^Method tests\\FunctionalTester\:\:grabTextFrom\(\) has parameter \$cssOrXPathOrRegex with no type specified\.$#'
identifier: missingType.parameter
Expand Down
62 changes: 47 additions & 15 deletions src/Codeception/Lib/Connector/Yii2.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
use yii\base\ExitException;
use yii\base\Security;
use yii\base\UserException;
use yii\mail\BaseMessage;
use yii\mail\BaseMailer;
use yii\mail\MailEvent;
use yii\mail\MessageInterface;
use yii\web\Application;
use yii\web\IdentityInterface;
use yii\web\Request as YiiRequest;
Expand All @@ -36,7 +38,22 @@ final class Yii2 extends Client
{
use Shared\PhpSuperGlobalsConverter;

public const CLEAN_METHODS = [
public const array MAIL_METHODS = [
self::MAIL_CATCH,
self::MAIL_EVENT_AFTER,
self::MAIL_EVENT_BEFORE,
self::MAIL_IGNORE
];

public const string MAIL_CATCH = 'catch';

public const string MAIL_EVENT_AFTER = 'after';

public const string MAIL_EVENT_BEFORE = 'before';

public const string MAIL_IGNORE = 'ignore';

public const array CLEAN_METHODS = [
self::CLEAN_RECREATE,
self::CLEAN_CLEAR,
self::CLEAN_FORCE_RECREATE,
Expand All @@ -47,54 +64,59 @@ final class Yii2 extends Client
* Clean the response object by recreating it.
* This might lose behaviors / event handlers / other changes that are done in the application bootstrap phase.
*/
public const CLEAN_RECREATE = 'recreate';
public const string CLEAN_RECREATE = 'recreate';

/**
* Same as recreate but will not warn when behaviors / event handlers are lost.
*/
public const CLEAN_FORCE_RECREATE = 'force_recreate';
public const string CLEAN_FORCE_RECREATE = 'force_recreate';

/**
* Clean the response object by resetting specific properties via its' `clear()` method.
* This will keep behaviors / event handlers, but could inadvertently leave some changes intact.
*
* @see \yii\web\Response::clear()
*/
public const CLEAN_CLEAR = 'clear';
public const string CLEAN_CLEAR = 'clear';

/**
* Do not clean the response, instead the test writer will be responsible for manually resetting the response in
* between requests during one test
*/
public const CLEAN_MANUAL = 'manual';
public const string CLEAN_MANUAL = 'manual';

/**
* @var string application config file
*/
public $configFile;
public string $configFile;

/**
* @var self::MAIL_CATCH|self::MAIL_IGNORE|self::MAIL_EVENT_AFTER|self::MAIL_EVENT_BEFORE method for handling mails
*/
public string $mailMethod;

/**
* @var string method for cleaning the response object before each request
*/
public $responseCleanMethod;
public string $responseCleanMethod;

/**
* @var string method for cleaning the request object before each request
*/
public $requestCleanMethod;
public string $requestCleanMethod;

/**
* @var string[] List of component names that must be recreated before each request
*/
public $recreateComponents = [];
public array $recreateComponents = [];

/**
* This option is there primarily for backwards compatibility.
* It means you cannot make any modification to application state inside your app, since they will get discarded.
*
* @var bool whether to recreate the whole application before each request
*/
public $recreateApplication = false;
public bool $recreateApplication = false;

/**
* @var bool whether to close the session in between requests inside a single test, if recreateApplication is set to true
Expand All @@ -108,7 +130,7 @@ final class Yii2 extends Client
public string|null $applicationClass = null;

/**
* @var list<BaseMessage>
* @var list<MessageInterface>
*/
private array $emails = [];

Expand Down Expand Up @@ -213,7 +235,7 @@ public function getInternalDomains(): array

/**
* @internal
* @return list<BaseMessage> List of sent emails
* @return list<MessageInterface> List of sent emails
*/
public function getEmails(): array
{
Expand Down Expand Up @@ -281,7 +303,17 @@ public function startApp(?\yii\log\Logger $logger = null): void
unset($config['container']);
}

$config = $this->mockMailer($config);
match ($this->mailMethod) {
self::MAIL_CATCH => $config = $this->mockMailer($config),
self::MAIL_EVENT_AFTER => $config['components']['mailer']['on ' . BaseMailer::EVENT_AFTER_SEND] = function (MailEvent $event): void {
if ($event->isSuccessful) {
$this->emails[] = $event->message;
}
},
self::MAIL_EVENT_BEFORE => $config['components']['mailer']['on ' . BaseMailer::EVENT_BEFORE_SEND] = fn (MailEvent $event) => $this->emails[] = $event->message,
self::MAIL_IGNORE => null// Do nothing
};

$app = Yii::createObject($config);
if (! $app instanceof \yii\base\Application) {
throw new ModuleConfigException($this, "Failed to initialize Yii2 app");
Expand Down Expand Up @@ -450,7 +482,7 @@ protected function mockMailer(array $config): array

$mailerConfig = [
'class' => TestMailer::class,
'callback' => function (BaseMessage $message): void {
'callback' => function (MessageInterface $message): void {
$this->emails[] = $message;
},
];
Expand Down
3 changes: 2 additions & 1 deletion src/Codeception/Lib/Connector/Yii2/TestMailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@

use Closure;
use yii\mail\BaseMailer;
use yii\symfonymailer\Message;

final class TestMailer extends BaseMailer
{
public $messageClass = \yii\symfonymailer\Message::class;
public $messageClass = Message::class;

public Closure $callback;

Expand Down
Loading