Page MenuHomePhorge

D3989.1775402885.diff
No OneTemporary

Authored By
Unknown
Size
26 KB
Referenced Files
None
Subscribers
None

D3989.1775402885.diff

diff --git a/docker/proxy/rootfs/etc/nginx/nginx.conf b/docker/proxy/rootfs/etc/nginx/nginx.conf
--- a/docker/proxy/rootfs/etc/nginx/nginx.conf
+++ b/docker/proxy/rootfs/etc/nginx/nginx.conf
@@ -140,7 +140,9 @@
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
- rewrite ^/\\.well-known/(caldav|carddav) https://\$server_name/iRony/ redirect;
+ location ~ ^/\\.well-known/(caldav|carddav)(.*)$ {
+ return 301 /iRony/$2;
+ }
location /iRony {
auth_request /auth;
diff --git a/src/config/activesync.php b/src/config/activesync.php
new file mode 100644
--- /dev/null
+++ b/src/config/activesync.php
@@ -0,0 +1,5 @@
+<?php
+
+return [
+ 'uri' => env('ACTIVESYNC_URI', 'https://proxy/Microsoft-Server-ActiveSync'),
+];
diff --git a/src/config/dav.php b/src/config/dav.php
--- a/src/config/dav.php
+++ b/src/config/dav.php
@@ -1,5 +1,5 @@
<?php
return [
- 'uri' => env('DAV_URI', 'http://kolab/dav'),
+ 'uri' => env('DAV_URI', 'https://proxy/'),
];
diff --git a/src/tests/Infrastructure/ActivesyncTest.php b/src/tests/Infrastructure/ActivesyncTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/ActivesyncTest.php
@@ -0,0 +1,172 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\TestCase;
+use App\Backends\IMAP;
+use App\Backends\LDAP;
+use App\User;
+
+class ActivesyncTest extends TestCase
+{
+ private ?\GuzzleHttp\Client $client = null;
+ private ?User $user = null;
+
+ private static function toWbxml($xml)
+ {
+ $outputStream = fopen("php://temp", 'r+');
+ $encoder = new \Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3);
+ $dom = new \DOMDocument();
+ $dom->loadXML($xml);
+ $encoder->encode($dom);
+ rewind($outputStream);
+ return stream_get_contents($outputStream);
+ }
+
+ private static function fromWbxml($binary)
+ {
+ $stream = fopen('php://memory', 'r+');
+ fwrite($stream, $binary);
+ rewind($stream);
+ $decoder = new \Syncroton_Wbxml_Decoder($stream);
+ return $decoder->decode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ if (!$this->user) {
+ $user = $this->getTestUser('jane@kolab.org');
+ $user->password = "simple123";
+ $user->save();
+ IMAP::createUser($user);
+ LDAP::createUser($user);
+ $this->user = $user;
+ }
+
+ if (!$this->client) {
+ $this->client = new \GuzzleHttp\Client([
+ 'http_errors' => false, // No exceptions
+ 'base_uri' => \config("activesync.uri"),
+ 'verify' => false,
+ 'auth' => [$this->user->email, 'simple123'],
+ 'connect_timeout' => 10,
+ 'timeout' => 10,
+ 'headers' => [
+ "Content-Type" => "application/xml; charset=utf-8",
+ "Depth" => "1",
+ ]
+ ]);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public static function tearDownAfterClass(): void
+ {
+ // $user = User::withTrashed()->where('email', 'jane@kolab.org')->first();
+ // IMAP::deleteUser($user);
+ // LDAP::deleteUser($user);
+ // $user->forceDelete();
+ }
+
+ public function testOptions()
+ {
+ $response = $this->client->request('OPTIONS', '');
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertStringContainsString('14', $response->getHeader('MS-Server-ActiveSync')[0]);
+ $this->assertStringContainsString('14.1', $response->getHeader('MS-ASProtocolVersions')[0]);
+ $this->assertStringContainsString('FolderSync', $response->getHeader('MS-ASProtocolCommands')[0]);
+ }
+
+ public function testFolderList()
+ {
+ $request = <<<EOF
+ <?xml version="1.0" encoding="utf-8"?>
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+ <FolderSync xmlns="uri:FolderHierarchy">
+ <SyncKey>0</SyncKey>
+ </FolderSync>
+ EOF;
+ $body = self::toWbxml($request);
+ $response = $this->client->request(
+ 'POST',
+ "?Cmd=FolderSync&User={$this->user->email}&DeviceId=v140Device&DeviceType=iphone",
+ [
+ 'headers' => [
+ "Content-Type" => "application/vnd.ms-sync.wbxml",
+ 'MS-ASProtocolVersion' => "14.0"
+ ],
+ 'body' => $body
+ ]
+ );
+ $this->assertEquals(200, $response->getStatusCode());
+ $result = self::fromWbxml($response->getBody())->saveXML();
+
+ print($result);
+ $this->assertStringContainsString('INBOX', $result);
+ //TODO for this to work we need to create the default folders in IMAP::createUser
+ // $this->assertStringContainsString('Drafts', $result);
+ // $this->assertStringContainsString('Sent', $result);
+ // $this->assertStringContainsString('Trash', $result);
+ // $this->assertStringContainsString('Calendar', $result);
+ // $this->assertStringContainsString('Contacts', $result);
+
+ //TODO extract the collectionid for the next step
+ }
+
+ public function testInitialSync()
+ {
+ $request = <<<EOF
+ <?xml version="1.0" encoding="utf-8"?>
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+ <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase">
+ <Collections>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>38b950ebd62cd9a66929c89615d0fc04</CollectionId>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>1</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ </Collections>
+ <WindowSize>16</WindowSize>
+ </Sync>
+ EOF;
+ $body = self::toWbxml($request);
+ $response = $this->client->request(
+ 'POST',
+ "?Cmd=Sync&User={$this->user->email}&DeviceId=v140Device&DeviceType=iphone",
+ [
+ 'headers' => [
+ "Content-Type" => "application/vnd.ms-sync.wbxml",
+ 'MS-ASProtocolVersion' => "14.0"
+ ],
+ 'body' => $body
+ ]
+ );
+ $this->assertEquals(200, $response->getStatusCode());
+ $dom = self::fromWbxml($response->getBody());
+ $collections = $dom->getElementsByTagName('Collection');
+ $this->assertEquals(1, $collections->length);
+ $collection = $collections->item(0);
+ $this->assertEquals("Class", $collection->childNodes->item(0)->nodeName);
+ $this->assertEquals("Email", $collection->childNodes->item(0)->nodeValue);
+ $this->assertEquals("SyncKey", $collection->childNodes->item(1)->nodeName);
+ $this->assertEquals("1", $collection->childNodes->item(1)->nodeValue);
+ $this->assertEquals("Status", $collection->childNodes->item(3)->nodeName);
+ $this->assertEquals("1", $collection->childNodes->item(3)->nodeValue);
+ }
+}
diff --git a/src/tests/Infrastructure/AutodiscoverTest.php b/src/tests/Infrastructure/AutodiscoverTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/AutodiscoverTest.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\TestCase;
+
+class AutodiscoverTest extends TestCase
+{
+ private \GuzzleHttp\Client $client;
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+ $this->client = new \GuzzleHttp\Client([
+ 'http_errors' => false, // No exceptions
+ 'base_uri' => "http://roundcube/",
+ 'verify' => false,
+ 'connect_timeout' => 10,
+ 'timeout' => 10,
+ ]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ parent::tearDown();
+ }
+
+ public function testWellKnownOutlook()
+ {
+ $body = <<<EOF
+ <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
+ <Request>
+ <EMailAddress>admin@example.local</EMailAddress>
+ <AcceptableResponseSchema>
+ http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a
+ </AcceptableResponseSchema>
+ </Request>
+ </Autodiscover>
+ EOF;
+ $response = $this->client->request('POST', 'autodiscover/autodiscover.xml', [
+ 'headers' => [
+ "Content-Type" => "text/xml; charset=utf-8"
+ ],
+ 'body' => $body
+ ]);
+ $this->assertEquals($response->getStatusCode(), 200);
+ $data = $response->getBody();
+ $this->assertTrue(str_contains($data, '<Server>example.local</Server>'));
+ $this->assertTrue(str_contains($data, 'admin@example.local'));
+ }
+
+ public function testWellKnownActivesync()
+ {
+ $body = <<<EOF
+ <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/mobilesync/requestschema/2006">
+ <Request>
+ <EMailAddress>admin@example.local</EMailAddress>
+ <AcceptableResponseSchema>
+ http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006
+ </AcceptableResponseSchema>
+ </Request>
+ </Autodiscover>
+ EOF;
+ $response = $this->client->request('POST', 'autodiscover/autodiscover.xml', [
+ 'headers' => [
+ "Content-Type" => "text/xml; charset=utf-8"
+ ],
+ 'body' => $body
+ ]);
+ $this->assertEquals($response->getStatusCode(), 200);
+ $data = $response->getBody();
+ $this->assertTrue(str_contains($data, '<Url>https://example.local/Microsoft-Server-ActiveSync</Url>'));
+ $this->assertTrue(str_contains($data, 'admin@example.local'));
+ }
+
+ public function testWellKnownMail()
+ {
+ $response = $this->client->request(
+ 'GET',
+ '.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=fred@example.com'
+ );
+ $this->assertEquals($response->getStatusCode(), 200);
+ }
+}
diff --git a/src/tests/Infrastructure/ChwalaTest.php b/src/tests/Infrastructure/ChwalaTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/ChwalaTest.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\TestCase;
+
+class ChwalaTest extends TestCase
+{
+ private \GuzzleHttp\Client $client;
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ //FIXME test users are probably not created in imap,
+ //which is why this fails. So we're using john@kolab.org for now.
+ // $user = $this->getTestUser('jane@kolabnow.com');
+ // $user->password = "simple123";
+ // $user->save();
+
+ $this->client = new \GuzzleHttp\Client([
+ 'base_uri' => "http://roundcube/chwala/",
+ 'verify' => false,
+ // 'auth' => [$user->email, $user->password],
+ 'auth' => ['john@kolab.org', 'simple123'],
+ 'connect_timeout' => 10,
+ 'timeout' => 10
+ ]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ // $this->deleteTestUser('jane@kolabnow.com');
+ parent::tearDown();
+ }
+
+ public function testAccess()
+ {
+ $response = $this->client->request('GET', 'api/?method=authenticate&version=4');
+ $this->assertEquals($response->getStatusCode(), 200);
+ $json = json_decode($response->getBody(), true);
+
+ $this->assertEquals('OK', $json['status']);
+ $token = $json['result']['token'];
+ $this->assertTrue(!empty($token));
+
+ //FIXME the session token doesn't seem to be required here?
+ $response = $this->client->request('GET', 'api/?method=mimetypes', [
+ 'headers' => [
+ 'X-Session_token' => $token
+ ]
+ ]);
+ $this->assertEquals($response->getStatusCode(), 200);
+ $json = json_decode($response->getBody(), true);
+ $this->assertEquals('OK', $json['status']);
+ $this->assertEquals('OK', $json['status']);
+ $this->assertContains('image/png', $json['result']['view']);
+ $this->assertArrayHasKey('text/plain', $json['result']['edit']);
+ }
+}
diff --git a/src/tests/Infrastructure/DavTest.php b/src/tests/Infrastructure/DavTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/DavTest.php
@@ -0,0 +1,304 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\TestCase;
+use App\Backends\IMAP;
+use App\Backends\LDAP;
+use App\User;
+
+class DavTest extends TestCase
+{
+ private ?\GuzzleHttp\Client $client = null;
+ private ?User $user = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ if (!$this->user) {
+ $user = $this->getTestUser('jane@kolab.org');
+ $user->password = "simple123";
+ $user->save();
+ IMAP::createUser($user);
+ LDAP::createUser($user);
+ $this->user = $user;
+ }
+
+ if (!$this->client) {
+ $this->client = new \GuzzleHttp\Client([
+ 'http_errors' => false, // No exceptions
+ 'base_uri' => \config("dav.uri"),
+ 'verify' => false,
+ 'auth' => [$this->user->email, 'simple123'],
+ 'connect_timeout' => 10,
+ 'timeout' => 10,
+ 'headers' => [
+ "Content-Type" => "application/xml; charset=utf-8",
+ "Depth" => "1",
+ ]
+ ]);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public static function tearDownAfterClass(): void
+ {
+ // $user = User::withTrashed()->where('email', 'jane@kolab.org')->first();
+ // IMAP::deleteUser($user);
+ // LDAP::deleteUser($user);
+ // $user->forceDelete();
+ }
+
+ public function testDiscoverPrincipal()
+ {
+ $user = $this->user;
+ $body = "<d:propfind xmlns:d='DAV:'><d:prop><d:current-user-principal/></d:prop></d:propfind>";
+ $response = $this->client->request('PROPFIND', '/iRony/', ['body' => $body]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>/iRony/principals/{$user->email}/</d:href>", $data);
+ $this->assertStringContainsString('<d:href>/iRony/calendars/</d:href>', $data);
+ $this->assertStringContainsString('<d:href>/iRony/addressbooks/</d:href>', $data);
+ }
+
+ /**
+ * This codepath is triggerd by MacOS CalDAV when it tries to login.
+ * Verify we don't crash and end up with a 500 status code.
+ */
+ public function testFailingLogin()
+ {
+ $body = "<d:propfind xmlns:d='DAV:'><d:prop><d:current-user-principal/></d:prop></d:propfind>";
+ $headers = [
+ "Content-Type" => "application/xml; charset=utf-8",
+ "Depth" => "1",
+ 'body' => $body,
+ 'auth' => ['invaliduser@kolab.org', 'invalid']
+ ];
+
+ $response = $this->client->request('PROPFIND', '/iRony/', $headers);
+ $this->assertEquals(403, $response->getStatusCode());
+ }
+
+ /**
+ * This codepath is triggerd by MacOS CardDAV when it tries to login.
+ * NOTE: This depends on the username_domain roundcube config option.
+ */
+ public function testShortlogin()
+ {
+ $this->markTestSkipped(
+ 'Shortlogins dont work with the nginx proxy.'
+ );
+ $body = "<d:propfind xmlns:d='DAV:'><d:prop><d:current-user-principal/></d:prop></d:propfind>";
+ $response = $this->client->request('PROPFIND', '/iRony/', [
+ 'body' => $body,
+ 'auth' => ['jane', 'simple123']
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+ }
+
+ public function testDiscoverCalendarHomeset()
+ {
+ $user = $this->user;
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <c:calendar-home-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = $this->client->request('PROPFIND', '/iRony/', ['body' => $body]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>/iRony/calendars/{$user->email}/</d:href>", $data);
+ }
+
+ public function testDiscoverCalendars()
+ {
+ $user = $this->user;
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:displayname />
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = $this->client->request('PROPFIND', "/iRony/calendars/{$user->email}", [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>/iRony/calendars/{$user->email}/</d:href>", $data);
+
+ $doc = new \DOMDocument('1.0', 'UTF-8');
+ $doc->loadXML($data);
+ $response = $doc->getElementsByTagName('response')->item(1);
+ $doc->getElementsByTagName('href')->item(0);
+
+ $this->assertEquals("d:href", $response->childNodes->item(0)->nodeName);
+ $href = $response->childNodes->item(0)->nodeValue;
+ return $href;
+ }
+
+ /**
+ * @depends testDiscoverCalendars
+ */
+ public function testPropfindCalendar($href)
+ {
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:owner/>
+ <d:current-user-principal/>
+ <d:current-user-privilege-set/>
+ <d:supported-report-set/>
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = $this->client->request('PROPFIND', $href, [
+ 'headers' => [
+ "Depth" => "0",
+ ],
+ 'body' => $body,
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>$href</d:href>", $data);
+ }
+
+ /**
+ * Thunderbird does this and relies on the WWW-Authenticate header response to
+ * start sending authenticated requests.
+ *
+ * @depends testDiscoverCalendars
+ */
+ public function testPropfindCalendarWithoutAuth($href)
+ {
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:owner/>
+ <d:current-user-principal/>
+ <d:current-user-privilege-set/>
+ <d:supported-report-set/>
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = $this->client->request('PROPFIND', $href, [
+ 'headers' => [
+ "Depth" => "0",
+ ],
+ 'body' => $body,
+ 'auth' => []
+ ]);
+ $this->assertEquals(401, $response->getStatusCode());
+ $this->assertStringContainsString('Basic realm=', $response->getHeader('WWW-Authenticate')[0]);
+ $data = $response->getBody();
+ $this->assertStringContainsString("<s:exception>Sabre\DAV\Exception\NotAuthenticated</s:exception>", $data);
+ }
+
+ /**
+ * Required for MacOS autoconfig
+ */
+ public function testOptions()
+ {
+ $user = $this->user;
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:displayname />
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = $this->client->request('OPTIONS', "/iRony/principals/{$user->email}/", ['body' => $body]);
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertStringContainsString('PROPFIND', $response->getHeader('Allow')[0]);
+ }
+
+ public function testWellKnown()
+ {
+ $user = $this->user;
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:displayname />
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ // The base URL needs to work as a redirect
+ $response = $this->client->request('PROPFIND', '/.well-known/caldav', [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body,
+ 'allow_redirects' => false
+ ]);
+ $this->assertEquals(301, $response->getStatusCode());
+ $redirectTarget = $response->getHeader('location')[0];
+ $this->assertEquals(\config('dav.uri') . "iRony/", $redirectTarget);
+
+ // Follow the redirect
+ $response = $this->client->request('PROPFIND', $redirectTarget, [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body,
+ 'allow_redirects' => false
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+
+ // Any URL should result in a redirect to the same path
+ $response = $this->client->request('PROPFIND', "/.well-known/caldav/calendars/{$user->email}", [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body,
+ 'allow_redirects' => false
+ ]);
+ $this->assertEquals(301, $response->getStatusCode());
+ $redirectTarget = $response->getHeader('location')[0];
+ //FIXME we have an extra slash that we don't technically want here
+ $this->assertEquals(\config('dav.uri') . "iRony//calendars/{$user->email}", $redirectTarget);
+
+ // Follow the redirect
+ $response = $this->client->request('PROPFIND', $redirectTarget, [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body,
+ 'allow_redirects' => false
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>/iRony/calendars/{$user->email}/</d:href>", $data);
+ }
+}
diff --git a/src/tests/Infrastructure/RoundcubeTest.php b/src/tests/Infrastructure/RoundcubeTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/RoundcubeTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\Browser;
+use Tests\TestCaseDusk;
+use App\Backends\IMAP;
+use App\Backends\LDAP;
+use App\User;
+
+class RoundcubeTest extends TestCaseDusk
+{
+ private ?User $user = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+ Browser::$baseUrl = "http://roundcube/roundcubemail/";
+
+ if (!$this->user) {
+ $user = $this->getTestUser('jane@kolab.org');
+ $user->password = "simple123";
+ $user->save();
+ IMAP::createUser($user);
+ LDAP::createUser($user);
+ $this->user = $user;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ parent::tearDown();
+ }
+
+ public function testLogin()
+ {
+ $this->browse(function (Browser $browser) {
+ $browser->visit('/')
+ ->type('#rcmloginuser', $this->user->email)
+ ->type('#rcmloginpwd', "simple123")
+ ->press('#rcmloginsubmit')
+ ->waitFor('#logo')
+ ->waitUntil('!rcmail.busy')
+ ->assertSee('Inbox');
+
+ $browser->press('.contacts')
+ ->waitUntil('!rcmail.busy')
+ ->assertVisible('#directorylist')
+ ->assertVisible('.addressbook.personal')
+ ->assertSee('Contacts');
+
+ $browser->press('.button-calendar')
+ ->waitUntil('!rcmail.busy')
+ ->assertSee('Calendar');
+
+ //TODO requires the default folders to be created
+ // $browser->press('.button-files')
+ // ->waitUntil('!rcmail.busy')
+ // ->assertSeeIn('#files-folder-list', 'Files');
+
+ $browser->press('.button-notes')
+ ->waitUntil('!rcmail.busy')
+ ->assertSeeIn('#notebooks-content', 'Notes');
+
+ $browser->press('.button-tasklist')
+ ->waitUntil('!rcmail.busy')
+ ->assertSee('Tasks');
+
+ $browser->press('.settings')
+ ->waitUntil('!rcmail.busy')
+ ->assertSee('Activesync');
+ // print($browser->dump());
+ });
+ }
+}

File Metadata

Mime Type
text/plain
Expires
Sun, Apr 5, 3:28 PM (8 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18833791
Default Alt Text
D3989.1775402885.diff (26 KB)

Event Timeline