]> BookStack Code Mirror - bookstack/blob - tests/ThemeTest.php
Applied StyleCI changes
[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     public function test_body_start_and_end_template_files_can_be_used()
258     {
259         $bodyStartStr = 'barry-fought-against-the-panther';
260         $bodyEndStr = 'barry-lost-his-fight-with-grace';
261
262         $this->usingThemeFolder(function (string $folder) use ($bodyStartStr, $bodyEndStr) {
263             $viewDir = theme_path('layouts/parts');
264             mkdir($viewDir, 0777, true);
265             file_put_contents($viewDir . '/base-body-start.blade.php', $bodyStartStr);
266             file_put_contents($viewDir . '/base-body-end.blade.php', $bodyEndStr);
267
268             $resp = $this->asEditor()->get('/');
269             $resp->assertSee($bodyStartStr);
270             $resp->assertSee($bodyEndStr);
271         });
272     }
273
274     protected function usingThemeFolder(callable $callback)
275     {
276         // Create a folder and configure a theme
277         $themeFolderName = 'testing_theme_' . rtrim(base64_encode(time()), '=');
278         config()->set('view.theme', $themeFolderName);
279         $themeFolderPath = theme_path('');
280         File::makeDirectory($themeFolderPath);
281
282         // Run provided callback with theme env option set
283         $this->runWithEnv('APP_THEME', $themeFolderName, function () use ($callback, $themeFolderName) {
284             call_user_func($callback, $themeFolderName);
285         });
286
287         // Cleanup the custom theme folder we created
288         File::deleteDirectory($themeFolderPath);
289     }
290 }
291
292 class MyCustomCommand extends Command
293 {
294     protected $signature = 'bookstack:test-custom-command';
295
296     public function handle()
297     {
298         $this->line('Command ran!');
299     }
300 }
Morty Proxy This is a proxified and sanitized view of the page, visit original site.