]> BookStack Code Mirror - bookstack/blob - tests/ThemeTest.php
Skip intermediate login page with single provider
[bookstack] / tests / ThemeTest.php
1 <?php
2
3 namespace Tests;
4
5 use BookStack\Actions\ActivityType;
6 use BookStack\Actions\DispatchWebhookJob;
7 use BookStack\Actions\Webhook;
8 use BookStack\Auth\User;
9 use BookStack\Entities\Models\Page;
10 use BookStack\Entities\Tools\PageContent;
11 use BookStack\Facades\Theme;
12 use BookStack\Theming\ThemeEvents;
13 use Illuminate\Console\Command;
14 use Illuminate\Http\Client\Request as HttpClientRequest;
15 use Illuminate\Http\Request;
16 use Illuminate\Http\Response;
17 use Illuminate\Support\Facades\Artisan;
18 use Illuminate\Support\Facades\File;
19 use Illuminate\Support\Facades\Http;
20 use League\CommonMark\ConfigurableEnvironmentInterface;
21
22 class ThemeTest extends TestCase
23 {
24     protected $themeFolderName;
25     protected $themeFolderPath;
26
27     public function test_translation_text_can_be_overridden_via_theme()
28     {
29         $this->usingThemeFolder(function () {
30             $translationPath = theme_path('/lang/en');
31             File::makeDirectory($translationPath, 0777, true);
32
33             $customTranslations = '<?php
34             return [\'books\' => \'Sandwiches\'];
35         ';
36             file_put_contents($translationPath . '/entities.php', $customTranslations);
37
38             $homeRequest = $this->actingAs($this->getViewer())->get('/');
39             $homeRequest->assertElementContains('header nav', 'Sandwiches');
40         });
41     }
42
43     public function test_theme_functions_file_used_and_app_boot_event_runs()
44     {
45         $this->usingThemeFolder(function ($themeFolder) {
46             $functionsFile = theme_path('functions.php');
47             app()->alias('cat', 'dog');
48             file_put_contents($functionsFile, "<?php\nTheme::listen(\BookStack\Theming\ThemeEvents::APP_BOOT, function(\$app) { \$app->alias('cat', 'dog');});");
49             $this->runWithEnv('APP_THEME', $themeFolder, function () {
50                 $this->assertEquals('cat', $this->app->getAlias('dog'));
51             });
52         });
53     }
54
55     public function test_event_commonmark_environment_configure()
56     {
57         $callbackCalled = false;
58         $callback = function ($environment) use (&$callbackCalled) {
59             $this->assertInstanceOf(ConfigurableEnvironmentInterface::class, $environment);
60             $callbackCalled = true;
61
62             return $environment;
63         };
64         Theme::listen(ThemeEvents::COMMONMARK_ENVIRONMENT_CONFIGURE, $callback);
65
66         $page = Page::query()->first();
67         $content = new PageContent($page);
68         $content->setNewMarkdown('# test');
69
70         $this->assertTrue($callbackCalled);
71     }
72
73     public function test_event_web_middleware_before()
74     {
75         $callbackCalled = false;
76         $requestParam = null;
77         $callback = function ($request) use (&$callbackCalled, &$requestParam) {
78             $requestParam = $request;
79             $callbackCalled = true;
80         };
81
82         Theme::listen(ThemeEvents::WEB_MIDDLEWARE_BEFORE, $callback);
83         $this->get('/login', ['Donkey' => 'cat']);
84
85         $this->assertTrue($callbackCalled);
86         $this->assertInstanceOf(Request::class, $requestParam);
87         $this->assertEquals('cat', $requestParam->header('donkey'));
88     }
89
90     public function test_event_web_middleware_before_return_val_used_as_response()
91     {
92         $callback = function (Request $request) {
93             return response('cat', 412);
94         };
95
96         Theme::listen(ThemeEvents::WEB_MIDDLEWARE_BEFORE, $callback);
97         $resp = $this->get('/login', ['Donkey' => 'cat']);
98         $resp->assertSee('cat');
99         $resp->assertStatus(412);
100     }
101
102     public function test_event_web_middleware_after()
103     {
104         $callbackCalled = false;
105         $requestParam = null;
106         $responseParam = null;
107         $callback = function ($request, Response $response) use (&$callbackCalled, &$requestParam, &$responseParam) {
108             $requestParam = $request;
109             $responseParam = $response;
110             $callbackCalled = true;
111             $response->header('donkey', 'cat123');
112         };
113
114         Theme::listen(ThemeEvents::WEB_MIDDLEWARE_AFTER, $callback);
115
116         $resp = $this->get('/login', ['Donkey' => 'cat']);
117         $this->assertTrue($callbackCalled);
118         $this->assertInstanceOf(Request::class, $requestParam);
119         $this->assertInstanceOf(Response::class, $responseParam);
120         $resp->assertHeader('donkey', 'cat123');
121     }
122
123     public function test_event_web_middleware_after_return_val_used_as_response()
124     {
125         $callback = function () {
126             return response('cat456', 443);
127         };
128
129         Theme::listen(ThemeEvents::WEB_MIDDLEWARE_AFTER, $callback);
130
131         $resp = $this->get('/login', ['Donkey' => 'cat']);
132         $resp->assertSee('cat456');
133         $resp->assertStatus(443);
134     }
135
136     public function test_event_auth_login_standard()
137     {
138         $args = [];
139         $callback = function (...$eventArgs) use (&$args) {
140             $args = $eventArgs;
141         };
142
143         Theme::listen(ThemeEvents::AUTH_LOGIN, $callback);
144         $this->post('/login', ['email' => 'admin@admin.com', 'password' => 'password']);
145
146         $this->assertCount(2, $args);
147         $this->assertEquals('standard', $args[0]);
148         $this->assertInstanceOf(User::class, $args[1]);
149     }
150
151     public function test_event_auth_register_standard()
152     {
153         $args = [];
154         $callback = function (...$eventArgs) use (&$args) {
155             $args = $eventArgs;
156         };
157         Theme::listen(ThemeEvents::AUTH_REGISTER, $callback);
158         $this->setSettings(['registration-enabled' => 'true']);
159
160         $user = User::factory()->make();
161         $this->post('/register', ['email' => $user->email, 'name' => $user->name, 'password' => 'password']);
162
163         $this->assertCount(2, $args);
164         $this->assertEquals('standard', $args[0]);
165         $this->assertInstanceOf(User::class, $args[1]);
166     }
167
168     public function test_event_webhook_call_before()
169     {
170         $args = [];
171         $callback = function (...$eventArgs) use (&$args) {
172             $args = $eventArgs;
173
174             return ['test' => 'hello!'];
175         };
176         Theme::listen(ThemeEvents::WEBHOOK_CALL_BEFORE, $callback);
177
178         Http::fake([
179             '*' => Http::response('', 200),
180         ]);
181
182         $webhook = new Webhook(['name' => 'Test webhook', 'endpoint' => 'https://example.com']);
183         $webhook->save();
184         $event = ActivityType::PAGE_UPDATE;
185         $detail = Page::query()->first();
186
187         dispatch((new DispatchWebhookJob($webhook, $event, $detail)));
188
189         $this->assertCount(5, $args);
190         $this->assertEquals($event, $args[0]);
191         $this->assertEquals($webhook->id, $args[1]->id);
192         $this->assertEquals($detail->id, $args[2]->id);
193
194         Http::assertSent(function (HttpClientRequest $request) {
195             return $request->isJson() && $request->data()['test'] === 'hello!';
196         });
197     }
198
199     public function test_add_social_driver()
200     {
201         Theme::addSocialDriver('catnet', [
202             'client_id'     => 'abc123',
203             'client_secret' => 'def456',
204         ], 'SocialiteProviders\Discord\DiscordExtendSocialite@handleTesting');
205
206         $this->assertEquals('catnet', config('services.catnet.name'));
207         $this->assertEquals('abc123', config('services.catnet.client_id'));
208         $this->assertEquals(url('/login/service/catnet/callback'), config('services.catnet.redirect'));
209
210         $loginResp = $this->get('/login');
211         $loginResp->assertSee('login/service/catnet');
212     }
213
214     public function test_add_social_driver_uses_name_in_config_if_given()
215     {
216         Theme::addSocialDriver('catnet', [
217             'client_id'     => 'abc123',
218             'client_secret' => 'def456',
219             'name'          => 'Super Cat Name',
220         ], 'SocialiteProviders\Discord\DiscordExtendSocialite@handleTesting');
221
222         $this->assertEquals('Super Cat Name', config('services.catnet.name'));
223         $loginResp = $this->get('/login');
224         $loginResp->assertSee('Super Cat Name');
225     }
226
227     public function test_add_social_driver_allows_a_configure_for_redirect_callback_to_be_passed()
228     {
229         Theme::addSocialDriver(
230             'discord',
231             [
232                 'client_id'     => 'abc123',
233                 'client_secret' => 'def456',
234                 'name'          => 'Super Cat Name',
235             ],
236             'SocialiteProviders\Discord\DiscordExtendSocialite@handle',
237             function ($driver) {
238                 $driver->with(['donkey' => 'donut']);
239             }
240         );
241
242         $loginResp = $this->get('/login/service/discord');
243         $redirect = $loginResp->headers->get('location');
244         $this->assertStringContainsString('donkey=donut', $redirect);
245     }
246
247     public function test_register_command_allows_provided_command_to_be_usable_via_artisan()
248     {
249         Theme::registerCommand(new MyCustomCommand());
250
251         Artisan::call('bookstack:test-custom-command', []);
252         $output = Artisan::output();
253
254         $this->assertStringContainsString('Command ran!', $output);
255     }
256
257     protected function usingThemeFolder(callable $callback)
258     {
259         // Create a folder and configure a theme
260         $themeFolderName = 'testing_theme_' . rtrim(base64_encode(time()), '=');
261         config()->set('view.theme', $themeFolderName);
262         $themeFolderPath = theme_path('');
263         File::makeDirectory($themeFolderPath);
264
265         call_user_func($callback, $themeFolderName);
266
267         // Cleanup the custom theme folder we created
268         File::deleteDirectory($themeFolderPath);
269     }
270 }
271
272 class MyCustomCommand extends Command
273 {
274     protected $signature = 'bookstack:test-custom-command';
275
276     public function handle()
277     {
278         $this->line('Command ran!');
279     }
280 }
Morty Proxy This is a proxified and sanitized view of the page, visit original site.