Test message rendering (#9460)

This commit is contained in:
Pablo Zmdl
2024-12-19 19:01:25 +01:00
committed by GitHub
parent 4896fb091d
commit 46d25161dc
18 changed files with 564 additions and 2 deletions

View File

@@ -1,6 +1,10 @@
services:
mailhost:
image: docker.io/greenmail/standalone
environment:
GREENMAIL_OPTS: '-Dgreenmail.setup.test.all -Dgreenmail.hostname=0.0.0.0 -Dgreenmail.auth.disabled -Dgreenmail.preload.dir=/emails'
volumes:
- "../tests/MessageRendering/data/greenmail:/emails"
browserhost:
image: docker.io/selenium/standalone-chromium
ports:
@@ -40,6 +44,17 @@ services:
command:
- .ci/run_tests.sh
test_message_rendering:
depends_on:
- mailhost
image: ghcr.io/roundcube/roundcubemail-testrunner:php8.3
environment:
RC_CONFIG_IMAP_HOST: 'tls://mailhost:3143'
volumes:
- '..:/app'
command:
- .ci/run_test_message_rendering.sh
codespell:
image: ghcr.io/roundcube/roundcubemail-testrunner:php8.3
volumes:

View File

@@ -0,0 +1,11 @@
#!/bin/bash -ex
if ! test -f config/config-test.inc.php; then
cp -v .ci/config-test.inc.php config/config-test.inc.php
fi
# Install dependencies, prefer highest.
composer update --prefer-dist --no-interaction --no-progress
# Execute tests.
vendor/bin/phpunit -c ./tests/MessageRendering/phpunit.xml --fail-on-warning --fail-on-risky

37
.github/workflows/message_rendering.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Message Rendering
on:
push:
pull_request:
permissions:
contents: read
jobs:
message_rendering:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
strategy:
fail-fast: false
name: Linux / PHP 8.3
steps:
- name: Checkout code
uses: actions/checkout@v4
# Run via docker compose because we can't run greenmail in a server here
# (it requires the testing emails to be present when starting but
# services are started before the repo is cloned). And instead of
# re-building what our compose-file contains we can just use it.
- name: Run tests via docker compose
run: docker compose -f .ci/compose.yaml run test_message_rendering
- name: Upload artifacts
uses: actions/upload-artifact@master
if: failure()
with:
name: Logs
path: logs/errors.log

3
.gitignore vendored
View File

@@ -34,3 +34,6 @@ skins/elastic/styles/_variables.less
.vscode
.DS_Store
.idea
# Ignore files used for local overriding.
/tests/MessageRendering/.env

View File

@@ -172,7 +172,7 @@ class rcmail_action_mail_show extends rcmail_action_mail_index
}
}
exit;
$rcmail->output->sendExit();
}
/**

11
tests/MessageRendering/.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
src/maildir/subscriptions
src/maildir/dovecot-uidvalidity
src/maildir/dovecot.*.log
src/maildir/dovecot.*.cache
src/maildir/dovecot.list.index
src/maildir/dovecot-uid*
src/maildir/.Drafts
src/maildir/.Junk
src/maildir/.Sent
src/maildir/.Trash
.phpunit.result.cache

View File

@@ -0,0 +1,69 @@
<?php
namespace Tests\MessageRendering;
/**
* Test class to test simple messages.
*/
class BasicMessagesTest extends MessageRenderingTestCase
{
/**
* Test that two text mime-parts with disposition "attachment" are shown as
* attachments.
*/
public function testList00()
{
$domxpath = $this->runAndGetHtmlOutputDomxpath('99839b8ec12482419372f1edafa9de75@woodcrest.local');
$this->assertSame('Lines', $this->getScrubbedSubject($domxpath));
$this->assertStringStartsWith('Plain text message body.', $this->getBody($domxpath));
$attchElems = $domxpath->query('//span[@class="attachment-name"]');
$this->assertCount(2, $attchElems, 'Attachments');
$this->assertStringStartsWith('lines.txt', $attchElems[0]->textContent);
$this->assertStringStartsWith('lines_lf.txt', $attchElems[1]->textContent);
}
/**
* Test that one inline image is not shown as attachment.
*/
public function testList01()
{
$domxpath = $this->runAndGetHtmlOutputDomxpath('3ef8a0120cd7dc2fd776468c8515e29a@domain.tld');
$this->assertSame('Test HTML with local and remote image', $this->getScrubbedSubject($domxpath));
$this->assertSame("Attached image: \nRemote image:", $this->getBody($domxpath));
$attchNames = $domxpath->query('//span[@class="attachment-name"]');
$this->assertCount(0, $attchNames, 'Attachments');
}
/**
* Test that text parts are shown and also listed as attachments, and that
* filenames are properly listed.
*/
public function testFilename()
{
$domxpath = $this->runAndGetHtmlOutputDomxpath('de75@tester.local');
$this->assertSame('Attachment filename encoding', $this->getScrubbedSubject($domxpath));
$msgParts = $domxpath->query('//div[@class="message-part"]');
$this->assertCount(3, $msgParts, 'Message text parts');
$this->assertSame("foo\nbar\ngna", $msgParts[0]->textContent);
$this->assertSame('潦੯慢ੲ湧', $msgParts[1]->textContent);
$this->assertSame("foo\nbar\ngna", $msgParts[2]->textContent);
$attchNames = $domxpath->query('//span[@class="attachment-name"]');
$this->assertCount(6, $attchNames, 'Attachments');
$this->assertSame('A011.txt', $attchNames[0]->textContent);
$this->assertSame('A012.txt', $attchNames[1]->textContent);
$this->assertSame('A014.txt', $attchNames[2]->textContent);
$this->assertSame('żółć.png', $attchNames[3]->textContent);
$this->assertSame('żółć.png', $attchNames[4]->textContent);
$this->assertSame('very very very very long very very very very long ćććććć very very very long name.txt', $attchNames[5]->textContent);
}
}

View File

@@ -0,0 +1,95 @@
<?php
namespace Tests\MessageRendering;
use Masterminds\HTML5;
use Roundcube\Tests\ActionTestCase;
use Roundcube\Tests\ExitException;
/**
* Class to base actual test classes on, which test specific message rendering.
*/
class MessageRenderingTestCase extends ActionTestCase
{
/**
* Get the body from the document, trimmed from surrounding whitespace.
*/
protected function getBody(\DOMXPath $domxpath): string
{
$bodyElem = $domxpath->query('//div[@id="messagebody"]');
$this->assertCount(1, $bodyElem, 'Message body');
return trim($bodyElem[0]->textContent);
}
/**
* Get the subject from the document, stripped by the prefix "Subject: ",
* the suffix "Open in new window", and trimmed from surrounding whitespace.
*/
protected function getScrubbedSubject(\DOMXPath $domxpath): string
{
$subjectElem = $domxpath->query('//h2[@class="subject"][1]');
$subject = preg_replace('/^\s*Subject:\s*(.*)\s*Open in new window$/', '$1', trim($subjectElem[0]->textContent));
return trim($subject);
}
/**
* Execute run() to render the message with the given $msgId.
*
* This is useful to check how rcmail_action_mail_show() renders messages.
* It requires a running dovecot to fetch the messages from. Messages need
* to be placed as individual files in
* `tests/src/emails/test@example/Mail/cur/`.
*/
protected function runAndGetHtmlOutputDomxpath(string $msgId): \DOMXPath
{
$imap_host = getenv('RC_CONFIG_IMAP_HOST') ?: 'tls://localhost:143';
$rcmail = \rcmail::get_instance();
// We need to overwrite the storage object, else storage_init() just
// returns the cached one (which might be a StorageMock instance).
$mockStorage = $rcmail->storage = null;
$rcmail->storage_init();
// Login our test user so we can fetch messages from the imap server.
$rcmail->login('test-message-rendering@localhost', 'pass', $imap_host);
$storage = $rcmail->get_storage();
$storage->set_options(['all_headers' => true]);
// We need to set the folder, else no message can be fetched.
$storage->set_folder('INBOX');
$output = $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', 'preview');
// TODO: Why do we need to set the skin manually?
$output->set_skin('elastic');
$action = new \rcmail_action_mail_show();
$this->assertTrue($action->checks());
$messagesList = $storage->list_messages();
// Find the UID of the wanted message.
$messageUid = null;
foreach ($messagesList as $messageHeaders) {
if ($messageHeaders->get('message-id') === "<{$msgId}>") {
$messageUid = $messageHeaders->uid;
break;
}
}
if ($messageUid === null) {
throw new \Exception("No message found in messages list with Message-Id '{$msgId}'");
}
// Prepare and trigger the rendering.
$_GET = ['_uid' => $messageUid];
$html = '';
try {
$action->run();
} catch (ExitException $e) {
$html = $output->getOutput();
}
// Reset the storage to the mocked one most other tests expect.
$rcmail->storage = $mockStorage;
// disabled_html_ns=true is a workaround for the performance issue
// https://github.com/Masterminds/html5-php/issues/181
$html5 = new HTML5(['disable_html_ns' => true]);
return new \DOMXPath($html5->loadHTML($html));
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Tests\MessageRendering;
/**
* Test class to test "interesting" messages.
*/
class SingleImageNoTextTest extends MessageRenderingTestCase
{
/**
* Test that of a multipart/mixed message which contains only one
* image, that image is shown.
*/
public function testShowMultipartMixedSingleImageToo()
{
$this->markTestSkipped('TBD: test for fixing GH issue 9443');
// This next comment line prevents phpstan from reporting this as
// unreachable code (technically it is right, but that's on purpose
// here...).
// @phpstan-ignore-next-line
$domxpath = $this->runAndGetHtmlOutputDomxpath('XXXXXXXXXXXXX@mx01.lytzenitmail.dk');
$this->assertSame('Not OK', $this->getScrubbedSubject($domxpath));
$attchNames = $domxpath->query('//span[@class="attachment-name"]');
$this->assertCount(1, $attchNames, 'Attachments');
$this->assertStringStartsWith('Resized_20240427_200026(1).jpeg', $attchNames[0]->textContent);
}
}

View File

@@ -0,0 +1,44 @@
<?php
/*
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Environment initialization script for unit tests |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
error_reporting(\E_ALL);
if (\PHP_SAPI != 'cli') {
exit('Not in shell mode (php-cli)');
}
if (!defined('INSTALL_PATH')) {
define('INSTALL_PATH', realpath(__DIR__ . '/../../') . '/');
}
define('ROUNDCUBE_TEST_MODE', true);
define('ROUNDCUBE_TEST_SESSION', microtime(true));
define('TESTS_DIR', __DIR__ . '/');
if (@is_dir(TESTS_DIR . 'config')) {
define('RCUBE_CONFIG_DIR', TESTS_DIR . 'config');
}
// Some tests depend on the way phpunit is executed
$_SERVER['SCRIPT_NAME'] = 'vendor/bin/phpunit';
require_once INSTALL_PATH . 'program/include/iniset.php';
rcmail::get_instance(0, 'test')->config->set('devel_mode', false);

View File

@@ -0,0 +1,34 @@
Message-Id: <XXXXXXXXXXXXX@mx01.lytzenitmail.dk>
Date:Mon, 06 May 2024 15:24:47 +0200
From: <+45XXXXXXXX@telenor.dk>
To: XXXXXX@jubii.dk
Subject: Not OK
Mime-Version:1.0
Content-Type:multipart/mixed;boundary="--------------------------------------------=_NextPart_0_24856"
this is a multi-part message in MIME format.
----------------------------------------------=_NextPart_0_24856
Content-Type:image/jpeg; name="Resized_20240427_200026(1).jpeg"
Content-Transfer-Encoding:base64
Content-Location:Resized_20240427_200026(1).jpeg
Content-ID:<Resized_20240427_200026(1)>
iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAADMElEQVR4nOzVwQnAIBQFQYXff81R
UkQCOyDj1YOPnbXWPmeTRef+/3O/OyBjzh3CD95BfqICMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0C
MK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0C
MK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0C
MK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0C
MK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0C
MK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0C
MK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0C
MK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0C
MK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0C
MK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0C
MK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0C
MK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0C
MK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0C
MK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMO0TAAD//2An
hf4QtqobAAAAAElFTkSuQmCC
----------------------------------------------=_NextPart_0_24856--

View File

@@ -0,0 +1,46 @@
From: "Thomas B." <thomas@roundcube.net>
To: Tom Tester <tb@tester.local>
Subject: Attachment filename encoding
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=1234;
Date: Fri, 23 May 2014 19:44:50 +0200
Message-ID: <de75@tester.local>
--1234
Content-Transfer-Encoding: base64
Content-Type: text/plain; name=A011.txt
Zm9vDQpiYXINCmduYQ==
--1234
Content-Transfer-Encoding: base64
Content-Type: text/plain; charset=UTF-16LE; name=A012.txt
Zm9vCmJhcgpnbmE=
--1234
Content-Transfer-Encoding: base64
Content-Type: text/plain; name=A013.txt
Content-Disposition: attachment; filename=A014.txt
Zm9vDQpiYXINCmduYQ==
--1234
Content-Transfer-Encoding: base64
Content-Type: image/png; charset=UTF-16LE;
name="=?UTF-8?Q?=C5=BC=C3=B3=C5=82=C4=87=2Epng?="
Zm9vCmJhcgpnbmE=
--1234
Content-Transfer-Encoding: base64
Content-Type: image/png; charset=UTF-16LE; name=A016.txt
Content-Disposition: attachment;
filename*=UTF-8''%C5%BC%C3%B3%C5%82%C4%87.png
Zm9vCmJhcgpnbmE=
--1234
Content-Transfer-Encoding: base64
Content-Type: text/plain; charset=iso-8859-1;
name="=?UTF-8?Q?very_very_very_very_long_very_very_very_very_long_=C4=87?=
=?UTF-8?Q?=C4=87=C4=87=C4=87=C4=87=C4=87_very_very_very_long_name=2Etxt?=
=?UTF-8?Q??="
Zm9vCmJhcgpnbmE=
--1234--

View File

@@ -0,0 +1,46 @@
Return-Path: <thomas@roundcube.net>
X-Original-To: tb@tester.local
From: "Thomas B." <thomas@roundcube.net>
To: Tom Tester <tb@tester.local>
Subject: Lines
Cc: test1@domain.tld, test2@domain.tld, test3@domain.tld, test4@domain.tld,
test5@domain.tld, test6@domain.tld, test7@domain.tld, test8@domain.tld,
test9@domain.tld, test10@domain.tld, test11@domain.tld, test12@domain.tld
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="=_8853bfb47b7da1852ac882e69cc724f3"
Date: Fri, 23 May 2014 19:44:50 +0200
Reply-To: hello@roundcube.net
Mail-Reply-To: hello@roundcube.net
Message-ID: <99839b8ec12482419372f1edafa9de75@woodcrest.local>
X-Sender: thomas@roundcube.net
--=_8853bfb47b7da1852ac882e69cc724f3
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=UTF-8;
format=flowed
Plain text message body.
--
Developer of Free Software
Sent with Roundcube Webmail - roundcube.net
--=_8853bfb47b7da1852ac882e69cc724f3
Content-Transfer-Encoding: base64
Content-Type: text/plain;
name=lines.txt
Content-Disposition: attachment;
filename=lines.txt;
size=13
Zm9vDQpiYXINCmduYQ==
--=_8853bfb47b7da1852ac882e69cc724f3
Content-Transfer-Encoding: base64
Content-Type: text/plain;
name=lines_lf.txt
Content-Disposition: attachment;
filename=lines_lf.txt;
size=11
Zm9vCmJhcgpnbmE=
--=_8853bfb47b7da1852ac882e69cc724f3--

View File

@@ -0,0 +1,89 @@
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="=_08f545e592541b77db5fd93e3267655b"
Date: Sat, 11 Jan 2020 19:31:21 +0100
From: test-from@domain.tld
To: test-to@domain.tld
Subject: Test HTML with local and remote image
Message-ID: <3ef8a0120cd7dc2fd776468c8515e29a@domain.tld>
--=_08f545e592541b77db5fd93e3267655b
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=US-ASCII;
format=flowed
Attached image:
Remote image:
--=_08f545e592541b77db5fd93e3267655b
Content-Type: multipart/related;
boundary="=_48ae8051b8f2cc74607d3ed75f3568af"
--=_48ae8051b8f2cc74607d3ed75f3568af
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=UTF-8
<html><head><meta http-equiv=3D"Content-Type" content=3D"text/html; charset=
=3DUTF-8" /></head><body style=3D'font-size: 10pt; font-family: Verdana,Gen=
eva,sans-serif'>
<p id=3D"attached">Attached image: <img src=3D"cid:15787674815e1a1479c8db24=
52476888@alec.pl" alt=3D"" /></p>
<p id=3D"remote">Remote image: <img src=3D"https://roundcube.net/images/rou=
ndcube_logo_icon.svg" width=3D"16" /></p>
<p><br /></p>
</body></html>
--=_48ae8051b8f2cc74607d3ed75f3568af
Content-Transfer-Encoding: base64
Content-ID: <15787674815e1a1479c8db2452476888@alec.pl>
Content-Type: image/x-icon;
name=favicon.ico
Content-Disposition: inline;
filename=favicon.ico;
size=2294
AAABAAIAEBAAAAEAIABoBAAAJgAAABAQAAABACAAaAQAAI4EAAAoAAAAEAAAACAAAAABACAAAAAA
AAAEAADDDgAAww4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRPQABUT0ADT0xAQW9hP8DkrDjA
/8E3Qf++NwP/vjcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVE9AAFRPQABUT0AiVE9AilNOQOhp
XD//6rE4//+/N+j/vjeK/743Iv++NwD/vjcAAAAAAAAAAABUT0AAVE9AAFRPQBJUT0BrVE9A11RP
QP5TTkD/aVw//+qxOP//vzf//743/v++N9f/vjdr/743Ev++NwD/vjcAVE9ACFRPQExUT0C/VE9A
+1RPQP9UT0D/U05A/2hcPv/qsDj//783//++N///vjf//743+/++N7//vjdM/743CFRPQKxUT0Dx
VE9A/1RPQP9UT0D/VE9A/1FNPv9sYUb/7LRA//++Nf//vjf//743//++N///vjf//7438f++N6xU
T0D/VE9A/1RPQP9UT0D/U04//1VRQv91cmf/uriz/+vZs//4yWb//r45//++Nf//vjf//743//++
N///vjf/VE9A/1RPQP9UT0D/U04//2hkWP+cmpT/0NDP/+bn5//l5uj/5+Pb/+/Vn//7xVX//742
//++N///vjf//743/1RPQP9UT0D/V1JD/4mGfv++vbv/0NDQ/+Hh4f/l5eX/5eXl/+Xl5v/l5uf/
6ODQ//PPhP/+vzv//743//++N/9UT0D/Uk0+/29rYP/FxcT/zc3N/9PT0//k5OT/5eXl/+Xl5f/l
5eX/5eXl/+Xl5//n49z/+cdf//++Nf//vjf/VE9Ay1JNPvqJh37/zc3N/8zMzP/X19f/5eXl/+Xl
5f/l5eX/5eXl/+Xl5f/l5eX/5eXn//PPhf//vTT6/743y1RPQBZRSzxum5mT4M3Nzf/MzMz/2NjY
/+Xl5f/l5eX/5eXl/+Xl5f/l5eX/5eXl/+Xl5//v1Z/g/70ybv++NxZUT0AA////AMbGxXTMzMz/
zMzM/9bW1v/l5eX/5eXl/+Xl5f/l5eX/5eXl/+Xl5f/l5eX/5uPdddb9/wD/vjcAAAAAAMzMzADM
zMw2zMzM7MzMzP/T09P/5OTk/+Xl5f/l5eX/5eXl/+Xl5f/l5eX/5eXl7OXl5Tfl5eUAAAAAAAAA
AADMzMwAzMzMBczMzJXMzMz/zs7O/+Dg4P/l5eX/5eXl/+Xl5f/l5eX/5eXl/+Xl5Zbk5OQF5OTk
AAAAAAAAAAAAzMzMAMzMzADMzMwYzMzMp8zMzPvW1tb/5OTk/+Xl5f/l5eX/5eXl++Xl5anl5eUZ
5eXlAOHh4QAAAAAAAAAAAAAAAADMzMwAzMzMAMzMzBLMzMx1zc3N1Nzc3Prl5eX65eXl1uXl5Xfl
5eUT5eXlAOLi4gAAAAAAAAAAAPgfAADwDwAAwAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAwAMAAMADAADAAwAA4AcAAPAPAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAADDDgAAww4A
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRPQABUT0ADT0xAQW9hP8DkrDjA/8E3Qf++NwP/vjcA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVE9AAFRPQABUT0AiVE9AilNOQOhpXD//6rE4//+/N+j/
vjeK/743Iv++NwD/vjcAAAAAAAAAAABUT0AAVE9AAFRPQBJUT0BrVE9A11RPQP5TTkD/aVw//+qx
OP//vzf//743/v++N9f/vjdr/743Ev++NwD/vjcAVE9ACFRPQExUT0C/VE9A+1RPQP9UT0D/U05A
/2hcPv/qsDj//783//++N///vjf//743+/++N7//vjdM/743CFRPQKxUT0DxVE9A/1RPQP9UT0D/
VE9A/1FNPv9sYUb/7LRA//++Nf//vjf//743//++N///vjf//7438f++N6xUT0D/VE9A/1RPQP9U
T0D/U04//1VRQv91cmf/uriz/+vZs//4yWb//r45//++Nf//vjf//743//++N///vjf/VE9A/1RP
QP9UT0D/U04//2hkWP+cmpT/0NDP/+bn5//l5uj/5+Pb/+/Vn//7xVX//742//++N///vjf//743
/1RPQP9UT0D/V1JD/4mGfv++vbv/0NDQ/+Hh4f/l5eX/5eXl/+Xl5v/l5uf/6ODQ//PPhP/+vzv/
/743//++N/9UT0D/Uk0+/29rYP/FxcT/zc3N/9PT0//k5OT/5eXl/+Xl5f/l5eX/5eXl/+Xl5//n
49z/+cdf//++Nf//vjf/VE9Ay1JNPvqJh37/zc3N/8zMzP/X19f/5eXl/+Xl5f/l5eX/5eXl/+Xl
5f/l5eX/5eXn//PPhf//vTT6/743y1RPQBZRSzxum5mT4M3Nzf/MzMz/2NjY/+Xl5f/l5eX/5eXl
/+Xl5f/l5eX/5eXl/+Xl5//v1Z/g/70ybv++NxZUT0AA////AMbGxXTMzMz/zMzM/9bW1v/l5eX/
5eXl/+Xl5f/l5eX/5eXl/+Xl5f/l5eX/5uPdddb9/wD/vjcAAAAAAMzMzADMzMw2zMzM7MzMzP/T
09P/5OTk/+Xl5f/l5eX/5eXl/+Xl5f/l5eX/5eXl7OXl5Tfl5eUAAAAAAAAAAADMzMwAzMzMBczM
zJXMzMz/zs7O/+Dg4P/l5eX/5eXl/+Xl5f/l5eX/5eXl/+Xl5Zbk5OQF5OTkAAAAAAAAAAAAzMzM
AMzMzADMzMwYzMzMp8zMzPvW1tb/5OTk/+Xl5f/l5eX/5eXl++Xl5anl5eUZ5eXlAOHh4QAAAAAA
AAAAAAAAAADMzMwAzMzMAMzMzBLMzMx1zc3N1Nzc3Prl5eX65eXl1uXl5Xfl5eUT5eXlAOLi4gAA
AAAAAAAAAPgfAADwDwAAwAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAMAAMAD
AADAAwAA4AcAAPAPAAA=
--=_48ae8051b8f2cc74607d3ed75f3568af--
--=_08f545e592541b77db5fd93e3267655b--

View File

@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<phpunit
bootstrap="bootstrap.php"
colors="true"
>
<extensions>
<extension class="Ergebnis\PHPUnit\SlowTestDetector\Extension"/>
</extensions>
<testsuites>
<testsuite name="tests">
<directory>.</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@@ -0,0 +1,18 @@
#!/bin/bash
testsdir=$(realpath $(dirname $0))
cont_name="roundcubetest-dovecot"
ROUNDCUBE_TEST_IMAP_SERVER_IMAGE="docker.io/dovecot/dovecot:latest"
test -f "$testsdir/.env" && source "$testsdir/.env"
docker run -it --rm -d --name "$cont_name" \
-p 143:143 \
-v "$testsdir/data/maildir:/srv/mail/test" \
-v "$testsdir/dovecot-maildir.conf:/etc/dovecot/conf.d/dovecot-maildir.conf" \
"$ROUNDCUBE_TEST_IMAP_SERVER_IMAGE" >/dev/null || exit 1
"$testsdir/../../vendor/bin/phpunit" -c "$testsdir/phpunit.xml" --fail-on-warning --fail-on-risky
docker stop "$cont_name" >/dev/null

View File

@@ -85,7 +85,7 @@ class OutputHtmlMock extends \rcmail_output_html
$this->header($header);
}
$this->output = $body;
$this->output .= $body;
throw new ExitException('Output sent', self::E_EXIT);
}

View File

@@ -22,6 +22,7 @@
<testsuite name="tests">
<directory>.</directory>
<exclude>Browser</exclude>
<exclude>MessageRendering</exclude>
</testsuite>
<testsuite name="plugins">
<directory>../plugins/acl/tests</directory>