diff --git a/config/search.php b/config/search.php index ee4d6bd98dd..7c01ac882f2 100644 --- a/config/search.php +++ b/config/search.php @@ -27,7 +27,7 @@ 'default' => [ 'driver' => 'local', - 'searchables' => 'all', + 'searchables' => 'content', 'fields' => ['title'], ], diff --git a/src/Assets/Asset.php b/src/Assets/Asset.php index f681dd376f8..45b86201072 100644 --- a/src/Assets/Asset.php +++ b/src/Assets/Asset.php @@ -1131,6 +1131,11 @@ public function getCpSearchResultBadge(): string return $this->container()->title(); } + public function getCpSearchResultIcon() + { + return 'assets'; + } + public function warmPresets() { if (! $this->isImage()) { diff --git a/src/Auth/User.php b/src/Auth/User.php index 4228af32861..f9ff31376eb 100644 --- a/src/Auth/User.php +++ b/src/Auth/User.php @@ -447,6 +447,11 @@ public function getCpSearchResultBadge(): string return __('User'); } + public function getCpSearchResultIcon(): string + { + return 'users'; + } + public function getElevatedSessionMethod(): string { if (! config('statamic.webauthn.allow_password_login_with_passkey', true) && $this->passkeys()->isNotEmpty()) { diff --git a/src/Contracts/Search/Result.php b/src/Contracts/Search/Result.php index e63b226590f..e5b15cc05a4 100644 --- a/src/Contracts/Search/Result.php +++ b/src/Contracts/Search/Result.php @@ -32,4 +32,6 @@ public function getCpTitle(): string; public function getCpUrl(): string; public function getCpBadge(): string; + + public function getCpIcon(): string; } diff --git a/src/Exceptions/AllSearchablesNotSupported.php b/src/Exceptions/AllSearchablesNotSupported.php new file mode 100644 index 00000000000..4b2b1091512 --- /dev/null +++ b/src/Exceptions/AllSearchablesNotSupported.php @@ -0,0 +1,11 @@ + 'all' is no longer supported. Please see the upgrade guide for more information: https://statamic.dev/getting-started/upgrade-guide/5-to-6"); + } +} diff --git a/src/Http/Controllers/CP/CommandPaletteController.php b/src/Http/Controllers/CP/CommandPaletteController.php index e1e64987457..6436f2f88e5 100644 --- a/src/Http/Controllers/CP/CommandPaletteController.php +++ b/src/Http/Controllers/CP/CommandPaletteController.php @@ -20,7 +20,7 @@ public function index() public function search(Request $request) { - return Search::index(locale: Site::selected()->handle()) + return Search::index(index: 'cp', locale: Site::selected()->handle()) ->ensureExists() ->search($request->query('q')) ->get() @@ -33,7 +33,7 @@ public function search(Request $request) ->url($result->getCpUrl()) ->badge($result->getCpBadge()) ->reference($result->getReference()) - // ->icon() // TODO: Make dynamic for entries/terms/users? + ->icon($result->getCpIcon()) ->toArray(); }) ->values(); diff --git a/src/Search/Commands/Update.php b/src/Search/Commands/Update.php index dba5d49f5a3..3e6ba18611a 100644 --- a/src/Search/Commands/Update.php +++ b/src/Search/Commands/Update.php @@ -73,7 +73,7 @@ private function getRequestedIndex() // They might have entered a name as it appears in the config, but if it // should be localized we'll get all of the localized versions. - if (collect(config('statamic.search.indexes'))->has($arg)) { + if (collect(config('statamic.search.indexes'))->put('cp', [])->has($arg)) { return $this->indexes()->filter(fn ($index) => Str::startsWith($index->name(), $arg))->all(); } diff --git a/src/Search/IndexManager.php b/src/Search/IndexManager.php index 674fb270fea..9e7567bb8dd 100644 --- a/src/Search/IndexManager.php +++ b/src/Search/IndexManager.php @@ -20,7 +20,7 @@ protected function invalidImplementationMessage($name) public function all() { - return collect($this->app['config']['statamic.search.indexes'])->flatMap(function ($config, $name) { + return $this->indexesConfig()->flatMap(function ($config, $name) { $sites = $config['sites'] ?? null; if ($sites === 'all') { @@ -127,11 +127,26 @@ protected function callLocalizedCustomCreator(array $config, string $name, ?stri return $this->customCreators[$config['driver']]($this->app, $config, $name, $locale); } + private function indexesConfig() + { + $config = collect($this->app['config']['statamic.search.indexes']); + + if (! $config->has('cp')) { + $config->put('cp', [ + 'driver' => 'local', + 'searchables' => ['content', 'users', 'addons'], + 'fields' => ['title'], + ]); + } + + return $config; + } + protected function getConfig($name) { $config = $this->app['config']; - if (! $index = $config["statamic.search.indexes.$name"]) { + if (! $index = $this->indexesConfig()->get($name)) { return null; } diff --git a/src/Search/Result.php b/src/Search/Result.php index 281dc423bce..0a22bf393f0 100644 --- a/src/Search/Result.php +++ b/src/Search/Result.php @@ -130,6 +130,11 @@ public function getCpBadge(): string return $this->searchable->getCpSearchResultBadge(); } + public function getCpIcon(): string + { + return $this->searchable->getCpSearchResultIcon(); + } + public function get($key, $fallback = null) { if ($key === 'date' && method_exists($this->searchable, 'date')) { diff --git a/src/Search/Search.php b/src/Search/Search.php index 890bc331728..db24c8a9272 100644 --- a/src/Search/Search.php +++ b/src/Search/Search.php @@ -39,6 +39,16 @@ public function registerSearchableProvider($class) app(Providers::class)->register($class); } + public function addCpSearchable($searchable) + { + Searchables::addCpSearchable($searchable); + } + + public function addContentSearchable($searchable) + { + Searchables::addContentSearchable($searchable); + } + public function __call($method, $parameters) { return $this->index()->$method(...$parameters); diff --git a/src/Search/Searchable.php b/src/Search/Searchable.php index 1b4b67cebae..38592947ac9 100644 --- a/src/Search/Searchable.php +++ b/src/Search/Searchable.php @@ -27,6 +27,11 @@ public function getCpSearchResultUrl() return $this->editUrl(); } + public function getCpSearchResultIcon() + { + return 'entry'; + } + public function toSearchResult(): Result { return new \Statamic\Search\Result($this, Str::before($this->reference(), '::')); diff --git a/src/Search/Searchables.php b/src/Search/Searchables.php index 8bdb1d6e0c6..4df925d39dd 100644 --- a/src/Search/Searchables.php +++ b/src/Search/Searchables.php @@ -7,6 +7,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\LazyCollection; use Statamic\Contracts\Search\Searchable; +use Statamic\Exceptions\AllSearchablesNotSupported; use Statamic\Search\Searchables\Providers; use Statamic\Support\Arr; use Statamic\Support\Str; @@ -16,6 +17,8 @@ class Searchables protected $index; protected $providers; protected $manager; + protected static array $cpSearchables = []; + protected static array $contentSearchables = []; public function __construct(Index $index) { @@ -23,6 +26,34 @@ public function __construct(Index $index) $this->providers = $this->makeProviders(); } + public static function addCpSearchable($searchable) + { + if (method_exists($searchable, 'handle')) { + $searchable = $searchable::handle(); + } + + static::$cpSearchables[] = $searchable; + } + + public static function clearCpSearchables() + { + static::$cpSearchables = []; + } + + public static function addContentSearchable($searchable) + { + if (method_exists($searchable, 'handle')) { + $searchable = $searchable::handle(); + } + + static::$contentSearchables[] = $searchable; + } + + public static function clearContentSearchables() + { + static::$contentSearchables = []; + } + private function makeProviders() { $manager = app(Providers::class); @@ -30,10 +61,21 @@ private function makeProviders() $providers = collect(Arr::wrap($this->index->config()['searchables'] ?? [])); if ($providers->contains('all')) { - return $manager->providers()->map(fn ($_, $key) => $manager->make($key, $this->index, ['*'])); + throw new AllSearchablesNotSupported(); } return $providers + ->flatMap(function ($key) { + if ($key === 'content') { + return ['collection:*', 'taxonomy:*', 'assets:*', ...static::$contentSearchables]; + } + + if ($key === 'addons') { + return static::$cpSearchables; + } + + return [$key]; + }) ->map(fn ($key) => ['provider' => Str::before($key, ':'), 'key' => Str::after($key, ':')]) ->groupBy('provider') ->map(fn ($items, $provider) => $manager->make($provider, $this->index, $items->map->key->all())); diff --git a/src/Taxonomies/LocalizedTerm.php b/src/Taxonomies/LocalizedTerm.php index b6eb3137320..79c81dea9c1 100644 --- a/src/Taxonomies/LocalizedTerm.php +++ b/src/Taxonomies/LocalizedTerm.php @@ -557,6 +557,11 @@ public function getCpSearchResultBadge() return $this->taxonomy()->title(); } + public function getCpSearchResultIcon() + { + return 'taxonomies'; + } + public function getBulkAugmentationReferenceKey(): ?string { if ($this->augmentationReferenceKey) { diff --git a/tests/Search/IndexManagerTest.php b/tests/Search/IndexManagerTest.php index c00c86e16e2..0de50496a57 100644 --- a/tests/Search/IndexManagerTest.php +++ b/tests/Search/IndexManagerTest.php @@ -36,7 +36,7 @@ public function it_gets_indexes() $manager = new IndexManager($this->app); - $this->assertEquals(['foo', 'bar_en', 'bar_fr', 'baz_en', 'baz_fr', 'baz_de'], $manager->all()->map->name()->values()->all()); + $this->assertEquals(['foo', 'bar_en', 'bar_fr', 'baz_en', 'baz_fr', 'baz_de', 'cp'], $manager->all()->map->name()->values()->all()); $this->assertInstanceOf(NullIndex::class, $foo = $manager->index('foo')); $this->assertEquals('foo', $foo->name()); @@ -75,4 +75,55 @@ public function it_gets_indexes() $this->assertEquals('Search index [bar] has not been configured for the [de] site.', $e->getMessage()); } } + + #[Test] + public function it_builds_the_cp_index_if_it_doesnt_exist_in_the_config() + { + config(['statamic.search.indexes' => [ + 'default' => [ + 'driver' => 'local', + 'searchables' => 'content', + 'fields' => ['title'], + ], + ]]); + + $manager = new IndexManager($this->app); + + $this->assertEquals(['default', 'cp'], $manager->all()->map->name()->values()->all()); + + $this->assertEquals([ + 'fields' => ['title'], + 'path' => storage_path('statamic/search'), + 'driver' => 'local', + 'searchables' => ['content', 'users', 'addons'], + ], $manager->index('cp')->config()); + } + + #[Test] + public function it_uses_the_cp_index_from_the_config_if_it_exists() + { + config(['statamic.search.indexes' => [ + 'default' => [ + 'driver' => 'local', + 'searchables' => 'content', + 'fields' => ['title'], + ], + 'cp' => [ + 'driver' => 'local', + 'searchables' => ['collections:pages', 'collections:blog'], + 'fields' => ['title', 'excerpt'], + ], + ]]); + + $manager = new IndexManager($this->app); + + $this->assertEquals(['default', 'cp'], $manager->all()->map->name()->values()->all()); + + $this->assertEquals([ + 'fields' => ['title', 'excerpt'], + 'path' => storage_path('statamic/search'), + 'driver' => 'local', + 'searchables' => ['collections:pages', 'collections:blog'], + ], $manager->index('cp')->config()); + } } diff --git a/tests/Search/Searchables/AssetsTest.php b/tests/Search/Searchables/AssetsTest.php index da1902bab7d..b8fb8db2df3 100644 --- a/tests/Search/Searchables/AssetsTest.php +++ b/tests/Search/Searchables/AssetsTest.php @@ -56,9 +56,9 @@ public function it_gets_assets($locale, $config, $expected) public static function assetsProvider() { return [ - 'all' => [ + 'content' => [ null, - ['searchables' => 'all'], + ['searchables' => 'content'], ['a', 'b', 'y', 'z'], ], 'all containers' => [ @@ -77,9 +77,9 @@ public static function assetsProvider() ['y', 'z'], ], - 'all, english' => [ + 'content, english' => [ 'en', - ['searchables' => 'all'], + ['searchables' => 'content'], ['a', 'b', 'y', 'z'], ], 'all containers, english' => [ @@ -98,9 +98,9 @@ public static function assetsProvider() ['y', 'z'], ], - 'all, french' => [ + 'content, french' => [ 'fr', - ['searchables' => 'all'], + ['searchables' => 'content'], ['a', 'b', 'y', 'z'], ], 'all containers, french' => [ @@ -138,7 +138,7 @@ public function it_can_use_a_custom_filter($filter) $d = tap(Asset::make()->container('images')->path('d.jpg'))->save(); $provider = $this->makeProvider(null, [ - 'searchables' => 'all', + 'searchables' => 'content', 'filter' => $filter, ]); @@ -185,7 +185,7 @@ private function normalizeSearchableKeys($keys) { // a bit of duplicated implementation logic. // but it makes the test look more like the real thing. - return collect($keys === 'all' ? ['*'] : $keys) + return collect($keys === 'content' ? ['*'] : $keys) ->map(fn ($key) => str_replace('assets:', '', $key)) ->all(); } diff --git a/tests/Search/Searchables/EntriesTest.php b/tests/Search/Searchables/EntriesTest.php index 003bff7c2c1..e2bddd86bf3 100644 --- a/tests/Search/Searchables/EntriesTest.php +++ b/tests/Search/Searchables/EntriesTest.php @@ -56,9 +56,9 @@ public function it_gets_entries($locale, $config, $expected) public static function entriesProvider() { return [ - 'all' => [ + 'content' => [ null, - ['searchables' => 'all'], + ['searchables' => 'content'], ['alfa', 'charlie', 'delta', 'foxtrot', 'xray', 'zulu'], ], 'all collections' => [ @@ -77,9 +77,9 @@ public static function entriesProvider() ['xray', 'zulu'], ], - 'all, english' => [ + 'content, english' => [ 'en', - ['searchables' => 'all'], + ['searchables' => 'content'], ['alfa', 'charlie', 'xray', 'zulu'], ], 'all collections, english' => [ @@ -98,9 +98,9 @@ public static function entriesProvider() ['xray', 'zulu'], ], - 'all, french' => [ + 'content, french' => [ 'fr', - ['searchables' => 'all'], + ['searchables' => 'content'], ['delta', 'foxtrot'], ], 'all collections, french' => [ @@ -133,7 +133,7 @@ public function it_can_use_a_custom_filter($filter) $e = EntryFactory::collection('blog')->slug('e')->create(); $provider = $this->makeProvider(null, [ - 'searchables' => 'all', + 'searchables' => 'content', 'filter' => $filter, ]); @@ -181,7 +181,7 @@ private function normalizeSearchableKeys($keys) { // a bit of duplicated implementation logic. // but it makes the test look more like the real thing. - return collect($keys === 'all' ? ['*'] : $keys) + return collect($keys === 'content' ? ['*'] : $keys) ->map(fn ($key) => str_replace('collection:', '', $key)) ->all(); } diff --git a/tests/Search/Searchables/TermsTest.php b/tests/Search/Searchables/TermsTest.php index 23b63cde015..6706bf508ce 100644 --- a/tests/Search/Searchables/TermsTest.php +++ b/tests/Search/Searchables/TermsTest.php @@ -77,9 +77,9 @@ public function it_gets_terms($locale, $config, $expected) public static function termsProvider() { return [ - 'all' => [ + 'content' => [ null, - ['searchables' => 'all'], + ['searchables' => 'content'], [ 'term::tags::alfa::en', 'term::tags::alfa::fr', @@ -120,9 +120,9 @@ public static function termsProvider() ], ], - 'all, english' => [ + 'content, english' => [ 'en', - ['searchables' => 'all'], + ['searchables' => 'content'], [ 'term::tags::alfa::en', 'term::tags::bravo::en', @@ -157,9 +157,9 @@ public static function termsProvider() ], ], - 'all, french' => [ + 'content, french' => [ 'fr', - ['searchables' => 'all'], + ['searchables' => 'content'], [ 'term::tags::alfa::fr', 'term::tags::bravo::fr', @@ -200,7 +200,7 @@ public function it_can_use_a_custom_filter($filter) $d = tap(Term::make('d')->taxonomy('tags')->dataForLocale('en', []))->save(); $provider = $this->makeProvider(null, [ - 'searchables' => 'all', + 'searchables' => 'content', 'filter' => $filter, ]); @@ -247,7 +247,7 @@ private function normalizeSearchableKeys($keys) { // a bit of duplicated implementation logic. // but it makes the test look more like the real thing. - return collect($keys === 'all' ? ['*'] : $keys) + return collect($keys === 'content' ? ['*'] : $keys) ->map(fn ($key) => str_replace('taxonomy:', '', $key)) ->all(); } diff --git a/tests/Search/Searchables/UsersTest.php b/tests/Search/Searchables/UsersTest.php index 975cedbb183..5b1277ba906 100644 --- a/tests/Search/Searchables/UsersTest.php +++ b/tests/Search/Searchables/UsersTest.php @@ -43,9 +43,9 @@ public function it_gets_users($locale, $config, $expected) public static function usersProvider() { return [ - 'all' => [ + 'content' => [ null, - ['searchables' => 'all'], + ['searchables' => 'content'], ['alfa@test.com', 'bravo@test.com'], ], 'all users' => [ @@ -54,9 +54,9 @@ public static function usersProvider() ['alfa@test.com', 'bravo@test.com'], ], - 'all, english' => [ + 'content, english' => [ 'en', - ['searchables' => 'all'], + ['searchables' => 'content'], ['alfa@test.com', 'bravo@test.com'], ], 'all users, english' => [ @@ -65,9 +65,9 @@ public static function usersProvider() ['alfa@test.com', 'bravo@test.com'], ], - 'all, french' => [ + 'content, french' => [ 'fr', - ['searchables' => 'all'], + ['searchables' => 'content'], ['alfa@test.com', 'bravo@test.com'], ], 'all users, french' => [ @@ -88,7 +88,7 @@ public function it_can_use_a_custom_filter($filter) $d = tap(User::make()->email('d@test.com'))->save(); $provider = $this->makeProvider(null, [ - 'searchables' => 'all', + 'searchables' => 'content', 'filter' => $filter, ]); @@ -135,7 +135,7 @@ private function normalizeSearchableKeys($keys) { // a bit of duplicated implementation logic. // but it makes the test look more like the real thing. - return collect($keys === 'all' ? ['*'] : $keys) + return collect($keys === 'content' ? ['*'] : $keys) ->map(fn ($key) => str_replace('users:', '', $key)) ->all(); } diff --git a/tests/Search/SearchablesTest.php b/tests/Search/SearchablesTest.php index 63aab79d0db..6fac9993a5e 100644 --- a/tests/Search/SearchablesTest.php +++ b/tests/Search/SearchablesTest.php @@ -9,6 +9,7 @@ use PHPUnit\Framework\Assert; use PHPUnit\Framework\Attributes\Test; use Statamic\Contracts\Entries\Entry as EntryContract; +use Statamic\Exceptions\AllSearchablesNotSupported; use Statamic\Facades\Asset; use Statamic\Facades\AssetContainer; use Statamic\Facades\Entry; @@ -44,44 +45,56 @@ public function setUp(): void AssetContainer::make('audio')->disk('audio')->save(); } + public function tearDown(): void + { + Searchables::clearCpSearchables(); + Searchables::clearContentSearchables(); + + parent::tearDown(); + } + + #[Test] + public function it_throws_an_exception_when_all_searchables_are_configured() + { + $this->expectException(AllSearchablesNotSupported::class); + + $this->makeSearchables(['searchables' => 'all']); + } + #[Test] - public function it_checks_all_providers_for_whether_an_item_is_searchable() + public function it_checks_content_providers_for_whether_an_item_is_searchable() { app(Providers::class)->register($entries = Mockery::mock(Entries::class)->makePartial()); app(Providers::class)->register($terms = Mockery::mock(Terms::class)->makePartial()); app(Providers::class)->register($assets = Mockery::mock(Assets::class)->makePartial()); - app(Providers::class)->register($users = Mockery::mock(Users::class)->makePartial()); $searchable = Mockery::mock(); - $searchables = $this->makeSearchables(['searchables' => 'all']); + $searchables = $this->makeSearchables(['searchables' => 'content']); // Check twice. // First time they'll all return false, so contains() will return false. - // Second time, assets will return true, so contains() will return true early, and users won't be checked. + // Second time, assets will return true, so contains() will return true early. $entries->shouldReceive('contains')->with($searchable)->twice()->andReturn(false, false); $terms->shouldReceive('contains')->with($searchable)->twice()->andReturn(false, false); $assets->shouldReceive('contains')->with($searchable)->twice()->andReturn(false, true); - $users->shouldReceive('contains')->with($searchable)->once()->andReturn(false); $this->assertFalse($searchables->contains($searchable)); $this->assertTrue($searchables->contains($searchable)); } #[Test] - public function all_searchables_include_entries_terms_assets_and_users() + public function content_searchables_include_entries_terms_and_assets() { app(Providers::class)->register($entries = Mockery::mock(Entries::class)->makePartial()); app(Providers::class)->register($terms = Mockery::mock(Terms::class)->makePartial()); app(Providers::class)->register($assets = Mockery::mock(Assets::class)->makePartial()); - app(Providers::class)->register($users = Mockery::mock(Users::class)->makePartial()); $entries->shouldReceive('provide')->andReturn(collect([$entryA = Entry::make(), $entryB = Entry::make()])); $terms->shouldReceive('provide')->andReturn(collect([$termA = Term::make(), $termB = Term::make()])); $assets->shouldReceive('provide')->andReturn(collect([$assetA = Asset::make(), $assetB = Asset::make()])); - $users->shouldReceive('provide')->andReturn(collect([$userA = User::make(), $userB = User::make()])); - $searchables = $this->makeSearchables(['searchables' => 'all']); + $searchables = $this->makeSearchables(['searchables' => 'content']); $everything = [ $entryA, @@ -90,8 +103,6 @@ public function all_searchables_include_entries_terms_assets_and_users() $termB, $assetA, $assetB, - $userA, - $userB, ]; $items = $searchables->all(); @@ -99,6 +110,68 @@ public function all_searchables_include_entries_terms_assets_and_users() $this->assertEquals($everything, $items->all()); } + #[Test] + public function content_searchables_dont_include_users() + { + app(Providers::class)->register($entries = Mockery::mock(Entries::class)->makePartial()); + app(Providers::class)->register($terms = Mockery::mock(Terms::class)->makePartial()); + app(Providers::class)->register($assets = Mockery::mock(Assets::class)->makePartial()); + app(Providers::class)->register($users = Mockery::mock(Users::class)->makePartial()); + + $entries->shouldReceive('provide')->andReturn(collect([$entryA = Entry::make(), $entryB = Entry::make()])); + $terms->shouldReceive('provide')->andReturn(collect([$termA = Term::make(), $termB = Term::make()])); + $assets->shouldReceive('provide')->andReturn(collect([$assetA = Asset::make(), $assetB = Asset::make()])); + $users->shouldNotReceive('provide'); + + $searchables = $this->makeSearchables(['searchables' => 'content']); + + $searchables->all()->each(fn ($item) => $this->assertNotInstanceOf(User::class, $item)); + } + + #[Test] + public function can_add_content_searchable() + { + app(Providers::class)->register($entries = Mockery::mock(Entries::class)->makePartial()); + app(Providers::class)->register($terms = Mockery::mock(Terms::class)->makePartial()); + app(Providers::class)->register($assets = Mockery::mock(Assets::class)->makePartial()); + + $entries->shouldReceive('provide')->andReturn(collect([$entry = Entry::make()])); + $terms->shouldReceive('provide')->andReturn(collect([$term = Term::make()])); + $assets->shouldReceive('provide')->andReturn(collect([$asset = Asset::make()])); + + $a = new TestCustomSearchable(['title' => 'Custom 1']); + $b = new TestCustomSearchable(['title' => 'Custom 2']); + app()->instance('all-custom-searchables', collect([$a, $b])); + + Search::registerSearchableProvider(TestCustomSearchables::class); + + $searchables = $this->makeSearchables(['searchables' => 'content']); + $this->assertEquals([$entry, $term, $asset], $searchables->all()->all()); + + Search::addContentSearchable(TestCustomSearchables::class); + + $searchables = $this->makeSearchables(['searchables' => 'content']); + $this->assertEquals([$entry, $term, $asset, $a, $b], $searchables->all()->all()); + } + + #[Test] + public function can_add_cp_searchable() + { + $a = new TestCustomSearchable(['title' => 'Custom 1']); + $b = new TestCustomSearchable(['title' => 'Custom 2']); + app()->instance('all-custom-searchables', collect([$a, $b])); + + Search::registerSearchableProvider(TestCustomSearchables::class); + + $searchables = $this->makeSearchables(['searchables' => 'addons']); + $this->assertEquals([], $searchables->all()->all()); + + Search::addCpSearchable(TestCustomSearchables::class); + + $searchables = $this->makeSearchables(['searchables' => 'addons']); + $this->assertEquals([$a, $b], $searchables->all()->all()); + } + #[Test] public function it_gets_searchables_from_specific_providers() {