1
0
mirror of https://github.com/flarum/core.git synced 2025-08-17 22:01:44 +02:00

Merge branch 'master' into dk/1236-user-preferences

This commit is contained in:
Daniël Klabbers
2020-06-26 09:50:10 +02:00
329 changed files with 7763 additions and 5804 deletions

View File

@@ -0,0 +1,65 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Tests\integration\extenders;
use Flarum\Console\AbstractCommand;
use Flarum\Extend;
use Flarum\Tests\integration\ConsoleTestCase;
class ConsoleTest extends ConsoleTestCase
{
/**
* @test
*/
public function custom_command_doesnt_exist_by_default()
{
$input = [
'command' => 'customTestCommand'
];
$this->assertEquals('Command "customTestCommand" is not defined.', $this->runCommand($input));
}
/**
* @test
*/
public function custom_command_exists_when_added()
{
$this->extend(
(new Extend\Console())
->command(CustomCommand::class)
);
$input = [
'command' => 'customTestCommand'
];
$this->assertEquals('Custom Command.', $this->runCommand($input));
}
}
class CustomCommand extends AbstractCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->setName('customTestCommand');
}
/**
* {@inheritdoc}
*/
protected function fire()
{
$this->info('Custom Command.');
}
}

View File

@@ -0,0 +1,114 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Tests\integration\extenders;
use Flarum\Extend;
use Flarum\Tests\integration\TestCase;
use Flarum\User\User;
class CsrfTest extends TestCase
{
protected $testUser = [
'username' => 'test',
'password' => 'too-obscure',
'email' => 'test@machine.local',
];
protected function prepDb()
{
$this->prepareDatabase([
'users' => [],
]);
}
/**
* @test
*/
public function create_user_post_needs_csrf_token_by_default()
{
$this->prepDb();
$response = $this->send(
$this->request('POST', '/api/users', [
'json' => [
'data' => [
'attributes' => $this->testUser
]
],
])
);
$this->assertEquals(400, $response->getStatusCode());
}
/**
* @test
*/
public function create_user_post_doesnt_need_csrf_token_if_whitelisted()
{
$this->extend(
(new Extend\Csrf)
->exemptPath('/api/users')
);
$this->prepDb();
$response = $this->send(
$this->request('POST', '/api/users', [
'json' => [
'data' => [
'attributes' => $this->testUser
]
],
])
);
$this->assertEquals(201, $response->getStatusCode());
$user = User::where('username', $this->testUser['username'])->firstOrFail();
$this->assertEquals(0, $user->is_email_confirmed);
$this->assertEquals($this->testUser['username'], $user->username);
$this->assertEquals($this->testUser['email'], $user->email);
}
/**
* @test
*/
public function post_to_unknown_route_will_cause_400_error_without_csrf_override()
{
$this->prepDb();
$response = $this->send(
$this->request('POST', '/api/fake/route/i/made/up')
);
$this->assertEquals(400, $response->getStatusCode());
}
/**
* @test
*/
public function csrf_matches_wildcards_properly()
{
$this->extend(
(new Extend\Csrf)
->exemptPath('/api/fake/*/up')
);
$this->prepDb();
$response = $this->send(
$this->request('POST', '/api/fake/route/i/made/up')
);
$this->assertEquals(404, $response->getStatusCode());
}
}

View File

@@ -0,0 +1,99 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Tests\integration\extenders;
use Flarum\Extend;
use Flarum\Group\Command\CreateGroup;
use Flarum\Group\Event\Created;
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
use Flarum\Tests\integration\TestCase;
use Flarum\User\User;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Contracts\Translation\Translator;
class EventTest extends TestCase
{
use RetrievesAuthorizedUsers;
protected function buildGroup()
{
$this->prepareDatabase([
'groups' => [
$this->adminGroup(),
],
'users' => [
$this->adminUser(),
],
]);
$bus = $this->app()->getContainer()->make(Dispatcher::class);
return $bus->dispatch(
new CreateGroup(User::find(1), ['attributes' => [
'nameSingular' => 'test group',
'namePlural' => 'test groups',
'color' => '#000000',
'icon' => 'fas fa-crown',
]])
);
}
/**
* @test
*/
public function custom_listener_doesnt_work_by_default()
{
$group = $this->buildGroup();
$this->assertEquals('test group', $group->name_singular);
}
/**
* @test
*/
public function custom_listener_works_with_closure()
{
$this->extend((new Extend\Event)->listen(Created::class, function (Created $event) {
$event->group->name_singular = 'modified group';
}));
$group = $this->buildGroup();
$this->assertEquals('modified group', $group->name_singular);
}
/**
* @test
*/
public function custom_listener_works_with_class_with_handle_method_and_can_inject_stuff()
{
// Because it injects a translator, this also tests that stuff can be injected into this callback.
$this->extend((new Extend\Event)->listen(Created::class, CustomListener::class));
$group = $this->buildGroup();
$this->assertEquals('core.group.admin', $group->name_singular);
}
}
class CustomListener
{
protected $translator;
public function __construct(Translator $translator)
{
$this->translator = $translator;
}
public function handle(Created $event)
{
$event->group->name_singular = $this->translator->trans('core.group.admin');
}
}

View File

@@ -0,0 +1,132 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Tests\integration\extenders;
use Flarum\Extend;
use Flarum\Mail\DriverInterface;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
use Flarum\Tests\integration\TestCase;
use Illuminate\Contracts\Validation\Factory;
use Illuminate\Support\MessageBag;
use Swift_NullTransport;
use Swift_Transport;
class MailTest extends TestCase
{
use RetrievesAuthorizedUsers;
protected function prepDb()
{
$this->prepareDatabase([
'users' => [
$this->adminUser(),
],
]);
}
/**
* @test
*/
public function drivers_are_unchanged_by_default()
{
$this->prepDb();
$response = $this->send(
$this->request('GET', '/api/mail/settings', [
'authenticatedAs' => 1,
])
);
$fields = json_decode($response->getBody(), true)['data']['attributes']['fields'];
// The custom driver does not exist
$this->assertArrayNotHasKey('custom', $fields);
// The SMTP driver has its normal fields
$this->assertEquals([
'mail_host' => '',
'mail_port' => '',
'mail_encryption' => '',
'mail_username' => '',
'mail_password' => '',
], $fields['smtp']);
}
/**
* @test
*/
public function added_driver_appears_in_mail_settings()
{
$this->extend(
(new Extend\Mail)
->driver('custom', CustomDriver::class)
);
$this->prepDb();
$response = $this->send(
$this->request('GET', '/api/mail/settings', [
'authenticatedAs' => 1,
])
);
$fields = json_decode($response->getBody(), true)['data']['attributes']['fields'];
$this->assertArrayHasKey('custom', $fields);
$this->assertEquals(['customSetting1' => ''], $fields['custom']);
}
/**
* @test
*/
public function adding_driver_with_duplicate_name_overrides_fields()
{
$this->extend(
(new Extend\Mail)
->driver('smtp', CustomDriver::class)
);
$this->prepDb();
$response = $this->send(
$this->request('GET', '/api/mail/settings', [
'authenticatedAs' => 1,
])
);
$requiredFields = json_decode($response->getBody(), true)['data']['attributes']['fields']['smtp'];
$this->assertEquals(['customSetting1' => ''], $requiredFields);
}
}
class CustomDriver implements DriverInterface
{
public function availableSettings(): array
{
return ['customSetting1' => ''];
}
public function validate(SettingsRepositoryInterface $settings, Factory $validator): MessageBag
{
return new MessageBag;
}
public function canSend(): bool
{
return false;
}
public function buildTransport(SettingsRepositoryInterface $settings): Swift_Transport
{
return new Swift_NullTransport;
}
}

View File

@@ -84,6 +84,44 @@ class MiddlewareTest extends TestCase
$this->assertEquals(200, $response->getStatusCode());
$this->assertArrayNotHasKey('X-First-Test-Middleware', $response->getHeaders());
}
/**
* @test
*/
public function can_insert_before_middleware()
{
$this->add_first_middleware();
$this->extend(
(new Extend\Middleware('forum'))->insertBefore(FirstTestMiddleware::class, SecondTestMiddleware::class)
);
$response = $this->send($this->request('GET', '/'));
$headers = $response->getHeaders();
$newMiddlewarePosition = array_search('X-Second-Test-Middleware', array_keys($headers));
$originalMiddlewarePosition = array_search('X-First-Test-Middleware', array_keys($headers));
$this->assertEquals(200, $response->getStatusCode());
$this->assertLessThan($newMiddlewarePosition, $originalMiddlewarePosition);
}
/**
* @test
*/
public function can_insert_after_middleware()
{
$this->add_first_middleware();
$this->extend(
(new Extend\Middleware('forum'))->insertAfter(FirstTestMiddleware::class, SecondTestMiddleware::class)
);
$response = $this->send($this->request('GET', '/'));
$headers = $response->getHeaders();
$newMiddlewarePosition = array_search('X-Second-Test-Middleware', array_keys($headers));
$originalMiddlewarePosition = array_search('X-First-Test-Middleware', array_keys($headers));
$this->assertEquals(200, $response->getStatusCode());
$this->assertGreaterThan($newMiddlewarePosition, $originalMiddlewarePosition);
}
}
class FirstTestMiddleware implements MiddlewareInterface

View File

@@ -0,0 +1,425 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Tests\integration\extenders;
use Carbon\Carbon;
use Flarum\Discussion\Discussion;
use Flarum\Extend;
use Flarum\Group\Group;
use Flarum\Post\AbstractEventPost;
use Flarum\Post\CommentPost;
use Flarum\Post\DiscussionRenamedPost;
use Flarum\Post\Post;
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
use Flarum\Tests\integration\TestCase;
use Flarum\User\User;
class ModelTest extends TestCase
{
use RetrievesAuthorizedUsers;
protected function prepDb()
{
$this->prepareDatabase([
'users' => [
$this->adminUser(),
$this->normalUser(),
],
'discussions' => []
]);
}
protected function prepPostsHierarchy()
{
$this->prepareDatabase([
'users' => [
$this->normalUser(),
],
'discussions' => [
['id' => 1, 'title' => 'Discussion with post', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 1, 'comment_count' => 1, 'is_private' => 0],
],
'posts' => [
['id' => 1, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
],
]);
}
/**
* @test
*/
public function custom_relationship_does_not_exist_by_default()
{
$this->prepDB();
$user = User::find(1);
$this->expectException(\BadMethodCallException::class);
$user->customRelation();
}
/**
* @test
*/
public function custom_hasOne_relationship_exists_if_added()
{
$this->extend(
(new Extend\Model(User::class))
->hasOne('customRelation', Discussion::class, 'user_id')
);
$this->prepDB();
$user = User::find(1);
$this->assertEquals([], $user->customRelation()->get()->toArray());
}
/**
* @test
*/
public function custom_hasMany_relationship_exists_if_added()
{
$this->extend(
(new Extend\Model(User::class))
->hasMany('customRelation', Discussion::class, 'user_id')
);
$this->prepDB();
$user = User::find(1);
$this->assertEquals([], $user->customRelation()->get()->toArray());
}
/**
* @test
*/
public function custom_belongsTo_relationship_exists_if_added()
{
$this->extend(
(new Extend\Model(User::class))
->belongsTo('customRelation', Discussion::class, 'user_id')
);
$this->prepDB();
$user = User::find(1);
$this->assertEquals([], $user->customRelation()->get()->toArray());
}
/**
* @test
*/
public function custom_relationship_exists_if_added()
{
$this->extend(
(new Extend\Model(User::class))
->relationship('customRelation', function (User $user) {
return $user->hasMany(Discussion::class, 'user_id');
})
);
$this->prepDB();
$user = User::find(1);
$this->assertEquals([], $user->customRelation()->get()->toArray());
}
/**
* @test
*/
public function custom_relationship_exists_and_can_return_instances_if_added()
{
$this->extend(
(new Extend\Model(User::class))
->relationship('customRelation', function (User $user) {
return $user->hasMany(Discussion::class, 'user_id');
})
);
$this->prepDB();
$this->prepareDatabase([
'discussions' => [
['id' => 1, 'title' => __CLASS__, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'first_post_id' => 1, 'comment_count' => 1]
]
]);
$user = User::find(1);
$this->assertNotEquals([], $user->customRelation()->get()->toArray());
$this->assertContains(json_encode(__CLASS__), json_encode($user->customRelation()->get()));
}
/**
* @test
*/
public function custom_relationship_is_inherited_to_child_classes()
{
$this->extend(
(new Extend\Model(Post::class))
->belongsTo('ancestor', Discussion::class, 'discussion_id')
);
$this->prepPostsHierarchy();
$post = CommentPost::find(1);
$this->assertInstanceOf(Discussion::class, $post->ancestor);
$this->assertEquals(1, $post->ancestor->id);
}
/**
* @test
*/
public function custom_relationship_prioritizes_child_classes_within_2_parent_classes()
{
$this->extend(
(new Extend\Model(Post::class))
->belongsTo('ancestor', User::class, 'user_id'),
(new Extend\Model(AbstractEventPost::class))
->belongsTo('ancestor', Discussion::class, 'discussion_id')
);
$this->prepPostsHierarchy();
$post = DiscussionRenamedPost::find(1);
$this->assertInstanceOf(Discussion::class, $post->ancestor);
$this->assertEquals(1, $post->ancestor->id);
}
/**
* @test
*/
public function custom_relationship_prioritizes_child_classes_within_child_class_and_immediate_parent()
{
$this->extend(
(new Extend\Model(AbstractEventPost::class))
->belongsTo('ancestor', Discussion::class, 'discussion_id'),
(new Extend\Model(DiscussionRenamedPost::class))
->belongsTo('ancestor', User::class, 'user_id')
);
$this->prepPostsHierarchy();
$post = DiscussionRenamedPost::find(1);
$this->assertInstanceOf(User::class, $post->ancestor);
$this->assertEquals(2, $post->ancestor->id);
}
/**
* @test
*/
public function custom_relationship_does_not_exist_if_added_to_unrelated_model()
{
$this->extend(
(new Extend\Model(User::class))
->relationship('customRelation', function (User $user) {
return $user->hasMany(Discussion::class, 'user_id');
})
);
$this->prepDB();
$this->prepareDatabase([
'groups' => [
$this->adminGroup()
]
]);
$group = Group::find(1);
$this->expectException(\BadMethodCallException::class);
$group->customRelation();
}
/**
* @test
*/
public function custom_default_attribute_doesnt_exist_if_not_set()
{
$group = new Group;
$this->app();
$this->assertNotEquals('Custom Default', $group->name_singular);
}
/**
* @test
*/
public function custom_default_attribute_works_if_set()
{
$this->extend(
(new Extend\Model(Group::class))
->default('name_singular', 'Custom Default')
);
$this->app();
$group = new Group;
$this->assertEquals('Custom Default', $group->name_singular);
}
/**
* @test
*/
public function custom_default_attribute_evaluated_at_runtime_if_callable()
{
$this->extend(
(new Extend\Model(Group::class))
->default('counter', function () {
static $counter = 0;
return ++$counter;
})
);
$this->app();
$group1 = new Group;
$group2 = new Group;
$this->assertEquals(1, $group1->counter);
$this->assertEquals(2, $group2->counter);
}
/**
* @test
*/
public function custom_default_attribute_is_inherited_to_child_classes()
{
$this->extend(
(new Extend\Model(Post::class))
->default('answer', 42)
);
$this->app();
$post = new CommentPost;
$this->assertEquals(42, $post->answer);
}
/**
* @test
*/
public function custom_default_attribute_inheritance_prioritizes_child_class()
{
$this->extend(
(new Extend\Model(Post::class))
->default('answer', 'dont do this'),
(new Extend\Model(AbstractEventPost::class))
->default('answer', 42),
(new Extend\Model(DiscussionRenamedPost::class))
->default('answer', 'ni!')
);
$this->app();
$post = new CustomPost;
$this->assertEquals(42, $post->answer);
$commentPost = new DiscussionRenamedPost;
$this->assertEquals('ni!', $commentPost->answer);
}
/**
* @test
*/
public function custom_default_attribute_doesnt_work_if_set_on_unrelated_model()
{
$this->extend(
(new Extend\Model(Group::class))
->default('name_singular', 'Custom Default')
);
$this->app();
$user = new User;
$this->assertNotEquals('Custom Default', $user->name_singular);
}
/**
* @test
*/
public function custom_date_attribute_doesnt_exist_by_default()
{
$post = new Post;
$this->app();
$this->assertNotContains('custom', $post->getDates());
}
/**
* @test
*/
public function custom_date_attribute_can_be_set()
{
$this->extend(
(new Extend\Model(Post::class))
->dateAttribute('custom')
);
$this->app();
$post = new Post;
$this->assertContains('custom', $post->getDates());
}
/**
* @test
*/
public function custom_date_attribute_is_inherited_to_child_classes()
{
$this->extend(
(new Extend\Model(Post::class))
->dateAttribute('custom')
);
$this->app();
$post = new CommentPost;
$this->assertContains('custom', $post->getDates());
}
/**
* @test
*/
public function custom_date_attribute_doesnt_work_if_set_on_unrelated_model()
{
$this->extend(
(new Extend\Model(Post::class))
->dateAttribute('custom')
);
$this->app();
$discussion = new Discussion;
$this->assertNotContains('custom', $discussion->getDates());
}
}
class CustomPost extends AbstractEventPost
{
/**
* {@inheritdoc}
*/
public static $type = 'customPost';
}

View File

@@ -0,0 +1,69 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Tests\integration\extenders;
use Flarum\Extend;
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
use Flarum\Tests\integration\TestCase;
use Flarum\User\DisplayName\DriverInterface;
use Flarum\User\User;
class UserTest extends TestCase
{
use RetrievesAuthorizedUsers;
protected function prepDb()
{
$this->prepareDatabase([
'users' => [
$this->adminUser(),
], 'settings' => [
['key' => 'display_name_driver', 'value' => 'custom'],
],
]);
}
/**
* @test
*/
public function username_display_name_driver_used_by_default()
{
$this->prepDb();
$user = User::find(1);
$this->assertEquals('admin', $user->displayName);
}
/**
* @test
*/
public function can_use_custom_display_name_driver()
{
$this->extend(
(new Extend\User)
->displayNameDriver('custom', CustomDisplayNameDriver::class)
);
$this->prepDb();
$user = User::find(1);
$this->assertEquals('admin@machine.local$$$suffix', $user->displayName);
}
}
class CustomDisplayNameDriver implements DriverInterface
{
public function displayName(User $user): string
{
return $user->email.'$$$suffix';
}
}