Skip to content

Make TestMailer proxying #117

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env-example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
COMPOSER_HOME=~/.config/composer
UID=1000
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
tests/_support
tests/_output
tests/cases/yii2-app-advanced/_data/db.sqlite
.env
11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
ARG PHP_VERSION="8.3-alpine"

FROM php:$PHP_VERSION

ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

RUN chmod +x /usr/local/bin/install-php-extensions \
&& apk add --no-cache git \
&& install-php-extensions gd intl zip intl pcov @composer

WORKDIR /var/www/html
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"codemix/yii2-localeurls": "^1.7",
"codeception/module-asserts": ">= 3.0",
"codeception/module-filesystem": "> 3.0",
"phpstan/phpstan": "^1.10"
"phpstan/phpstan": "^1.10",
"yiisoft/yii2-swiftmailer": "^2.0.0"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect. Why are we adding swiftmailer?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right.. it isn't needed, but phpstan was failing otherwise

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because I think that if we worry about symfonymailer we should worry about swiftmailer
and symfonymailer comes from dependecy to yiisoft/yii2-app-advanced

I actually still do not think that we need to set NullTransports to them, because again, TestMailer class overwrites TestMailer::sendMessage() not to send, so it should fail only if MessageInterface::send() does the sending itself, but then no amount of NullTransports fix it

},
"autoload":{
"classmap": ["src/"]
Expand Down
41 changes: 41 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
services:

php80: &phpbase
user: ${UID}
volumes:
- ${COMPOSER_HOME}:/.config/composer:delegated
# Mount source-code for development
- ./:/var/www/html
env_file:
- .env
environment:
- XDEBUG_CONFIG=client_host=host.docker.internal
extra_hosts:
- "host.docker.internal:host-gateway"
build:
context: ./
dockerfile: Dockerfile
args:
- PHP_VERSION=8.0-alpine

php81:
<<: *phpbase
build:
args:
- PHP_VERSION=8.1-alpine
php82:
<<: *phpbase
build:
args:
- PHP_VERSION=8.2-alpine

php83:
<<: *phpbase
build:
args:
- PHP_VERSION=8.3-alpine
php84:
<<: *phpbase
build:
args:
- PHP_VERSION=8.4-alpine
36 changes: 17 additions & 19 deletions src/Codeception/Lib/Connector/Yii2.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use yii\base\ExitException;
use yii\base\Security;
use yii\base\UserException;
use yii\helpers\ArrayHelper;
use yii\mail\MessageInterface;
use yii\web\Application;
use yii\web\ErrorHandler;
Expand Down Expand Up @@ -422,33 +423,30 @@ protected function encodeCookies(
*/
protected function mockMailer(array $config): array
{
// options that make sense for mailer mock
$allowedOptions = [
'htmlLayout',
'textLayout',
'messageConfig',
'messageClass',
'useFileTransport',
'fileTransportPath',
'fileTransportCallback',
'view',
'viewPath',
];

$mailerConfig = [
'class' => TestMailer::class,
'callback' => function (MessageInterface $message) {
$this->emails[] = $message;
}
},
'mailer' => [
'class' => \yii\symfonymailer\Mailer::class
]
];

if (isset($config['components']['mailer']) && is_array($config['components']['mailer'])) {
foreach ($config['components']['mailer'] as $name => $value) {
if (in_array($name, $allowedOptions, true)) {
$mailerConfig[$name] = $value;
}
}
$mailerConfig['mailer'] = ArrayHelper::merge($mailerConfig['mailer'], $config['components']['mailer']);
}

/**
* Set transports only if known mailers that have this field is used
*/
if ($mailerConfig['mailer']['class'] instanceof \yii\symfonymailer\Mailer) {
$mailerConfig['mailer']['transport'] = \Symfony\Component\Mailer\Transport\NullTransport::class;
}
if ($mailerConfig['mailer']['class'] instanceof \yii\swiftmailer\Mailer) {
$mailerConfig['mailer']['transport'] = \Swift_Transport_NullTransport::class;
}

$config['components']['mailer'] = $mailerConfig;

return $config;
Expand Down
92 changes: 87 additions & 5 deletions src/Codeception/Lib/Connector/Yii2/TestMailer.php
Original file line number Diff line number Diff line change
@@ -1,24 +1,106 @@
<?php
namespace Codeception\Lib\Connector\Yii2;

use ReflectionClass;
use yii\base\BaseObject;
use yii\base\InvalidConfigException;
use yii\base\UnknownMethodException;
use yii\base\UnknownPropertyException;
use yii\di\Instance;
use yii\mail\BaseMailer;
use yii\mail\MailerInterface;
use yii\mail\MessageInterface;

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

/**
* @var \Closure
*/
public $callback;

protected function sendMessage($message)
/**
* @var string|array|MailerInterface Mailer config or component to send mail out in the end
*/
public $mailer = 'mailer';

/**
* @inheritdoc
* @throws InvalidConfigException
*/
public function init(): void
{
parent::init();
$this->mailer = Instance::ensure($this->mailer, MailerInterface::class);
}

/**
* @param string $name
* @param array $params
* @return mixed
*/
public function __call($name, $params)
{
try {
return parent::__call($name, $params);
} catch (UnknownMethodException $e) {
return call_user_func_array([$this->mailer, $name], $params);
}
}

/**
* @param string $name
* @return mixed
*/
public function __get($name)
{
try {
return parent::__get($name);
} catch (UnknownPropertyException $e) {
return $this->mailer->{$name};
}
}

/**
* @param string $name
* @param mixed $value
*/
public function __set($name, $value)
{
try {
parent::__set($name, $value);
} catch (UnknownPropertyException $e) {
$this->mailer->{$name} = $value;
}
}

/**
* @inheritdoc
*/
public function compose($view = null, array $params = []): MessageInterface
{
$message = $this->mailer->compose($view, $params);

if ($message instanceof BaseObject && $message->canSetProperty('mailer')) {
/** @phpstan-ignore property.notFound */
$message->mailer = $this;
} else {
$reflection = new ReflectionClass($message);
if ($reflection->hasProperty('mailer')) {
/** @phpstan-ignore property.notFound */
$message->mailer = $this;
}
}

return $message;
}

protected function sendMessage($message): bool
{
call_user_func($this->callback, $message);
return true;
}
protected function saveMessage($message)

protected function saveMessage($message): bool
{
call_user_func($this->callback, $message);
return true;
Expand Down