import * as React from 'react'; import RepositoryApi, { InstallMode } from '@joplin/lib/services/plugins/RepositoryApi'; import { afterAllCleanUp, afterEachCleanUp, mockMobilePlatform, setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils'; import { render, screen, userEvent, waitFor } from '@testing-library/react-native'; import '@testing-library/react-native/extend-expect'; import SearchPlugins from './SearchPlugins'; import Setting from '@joplin/lib/models/Setting'; import PluginService, { PluginSettings } from '@joplin/lib/services/plugins/PluginService'; import { useMemo } from 'react'; import pluginServiceSetup from './testUtils/pluginServiceSetup'; import newRepoApi from './testUtils/newRepoApi'; interface WrapperProps { repoApi: RepositoryApi; repoApiInitialized?: boolean; pluginSettings?: PluginSettings; onUpdatePluginStates?: (states: PluginSettings)=> void; } const noOpFunction = ()=>{}; const SearchWrapper = (props: WrapperProps) => { const serializedPluginSettings = useMemo(() => { return PluginService.instance().serializePluginSettings(props.pluginSettings ?? {}); }, [props.pluginSettings]); return ( ); }; const expectSearchResultCountToBe = async (count: number) => { await waitFor(() => { expect(screen.queryAllByTestId('plugin-card')).toHaveLength(count); }); }; describe('SearchPlugins', () => { beforeEach(async () => { await setupDatabaseAndSynchronizer(0); await switchClient(0); pluginServiceSetup(); }); afterEach(() => afterEachCleanUp()); afterAll(() => afterAllCleanUp()); it('should find results', async () => { const repoApi = await newRepoApi(InstallMode.Default); render(); const searchBox = screen.queryByPlaceholderText('Search'); expect(searchBox).toBeVisible(); // No plugin cards should be visible by default expect(screen.queryAllByTestId('plugin-card')).toHaveLength(0); const user = userEvent.setup(); await user.type(searchBox, 'backlinks'); // Should find one result await expectSearchResultCountToBe(1); // Clearing the search input should hide all results await user.clear(searchBox); await expectSearchResultCountToBe(0); // Typing a space should show all results await user.type(searchBox, ' '); await waitFor(() => { expect(screen.queryAllByTestId('plugin-card').length).toBeGreaterThan(2); }); }); it('should only show recommended plugin search results on iOS-like environments', async () => { // iOS uses restricted install mode const repoApi = await newRepoApi(InstallMode.Restricted); render(); const searchBox = screen.queryByPlaceholderText('Search'); expect(searchBox).toBeVisible(); const user = userEvent.setup(); await user.type(searchBox, 'abc'); // Should find recommended plugins await expectSearchResultCountToBe(1); // Should not find non-recommended plugins await user.clear(searchBox); await user.type(searchBox, 'backlinks'); await expectSearchResultCountToBe(0); await user.clear(searchBox); await user.type(searchBox, ' '); await expectSearchResultCountToBe(1); expect(screen.getByText(/ABC Sheet Music/i)).toBeTruthy(); expect(screen.queryByText(/backlink/i)).toBeNull(); }); it('should mark incompatible plugins as incompatible', async () => { const mock = mockMobilePlatform('android'); const repoApi = await newRepoApi(InstallMode.Default); render(); const searchBox = screen.queryByPlaceholderText('Search'); const user = userEvent.setup(); await user.type(searchBox, 'abc'); await expectSearchResultCountToBe(1); expect(screen.queryByText('Incompatible')).toBeNull(); await user.clear(searchBox); await user.type(searchBox, 'side bar toggle'); await expectSearchResultCountToBe(1); expect(await screen.findByText(/Note list and side bar/i)).toBeVisible(); expect(await screen.findByText('Incompatible')).toBeVisible(); mock.reset(); }); });