forked from WordPress/gutenberg
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve (and relax) search vs direct URL entry detection in Link Cont…
…rol (WordPress#51011) * Restirct what matches as potentially being a “url” * Remove direct entry results from coming up in entity search suggestions * Make is-url-like stricter * Add initial tests for isURLLike * Improve code with tests and adding check for TLDs * Simply implementation * Fix tests * Test for only showing URL result when searching for URL like * Improve test criteria for URL-like in tests * Augment tests for entity search
- Loading branch information
Showing
4 changed files
with
198 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -196,8 +196,7 @@ describe( 'Basic rendering', () => { | |
within( resultsList ).getAllByRole( 'option' ); | ||
|
||
expect( searchResultElements ).toHaveLength( | ||
// The fauxEntitySuggestions length plus the 'Press ENTER to add this link' button. | ||
fauxEntitySuggestions.length + 1 | ||
fauxEntitySuggestions.length | ||
); | ||
|
||
// Step down into the search results, highlighting the first result item. | ||
|
@@ -440,44 +439,87 @@ describe( 'Searching for a link', () => { | |
expect( screen.queryByRole( 'presentation' ) ).not.toBeInTheDocument(); | ||
} ); | ||
|
||
it( 'should display only search suggestions when current input value is not URL-like', async () => { | ||
const user = userEvent.setup(); | ||
const searchTerm = 'Hello world'; | ||
const firstSuggestion = fauxEntitySuggestions[ 0 ]; | ||
it.each( [ 'With spaces', 'Uppercase', 'lowercase' ] )( | ||
'should display only search suggestions (and not URL result type) when current input value (e.g. %s) is not URL-like', | ||
async ( searchTerm ) => { | ||
const user = userEvent.setup(); | ||
const firstSuggestion = fauxEntitySuggestions[ 0 ]; | ||
|
||
render( <LinkControl /> ); | ||
render( <LinkControl /> ); | ||
|
||
// Search Input UI. | ||
const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); | ||
// Search Input UI. | ||
const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); | ||
|
||
// Simulate searching for a term. | ||
await user.type( searchInput, searchTerm ); | ||
// Simulate searching for a term. | ||
await user.type( searchInput, searchTerm ); | ||
|
||
const searchResultElements = within( | ||
await screen.findByRole( 'listbox', { | ||
name: /Search results for.*/, | ||
} ) | ||
).getAllByRole( 'option' ); | ||
const searchResultElements = within( | ||
await screen.findByRole( 'listbox', { | ||
name: /Search results for.*/, | ||
} ) | ||
).getAllByRole( 'option' ); | ||
|
||
expect( searchResultElements ).toHaveLength( | ||
fauxEntitySuggestions.length | ||
); | ||
expect( searchResultElements ).toHaveLength( | ||
fauxEntitySuggestions.length | ||
); | ||
|
||
expect( searchInput ).toHaveAttribute( 'aria-expanded', 'true' ); | ||
expect( searchInput ).toHaveAttribute( 'aria-expanded', 'true' ); | ||
|
||
// Check that a search suggestion shows up corresponding to the data. | ||
expect( searchResultElements[ 0 ] ).toHaveTextContent( | ||
firstSuggestion.title | ||
); | ||
expect( searchResultElements[ 0 ] ).toHaveTextContent( | ||
firstSuggestion.type | ||
); | ||
// Check that a search suggestion shows up corresponding to the data. | ||
expect( searchResultElements[ 0 ] ).toHaveTextContent( | ||
firstSuggestion.title | ||
); | ||
expect( searchResultElements[ 0 ] ).toHaveTextContent( | ||
firstSuggestion.type | ||
); | ||
|
||
// The fallback URL suggestion should not be shown when input is not URL-like. | ||
expect( | ||
searchResultElements[ searchResultElements.length - 1 ] | ||
).not.toHaveTextContent( 'URL' ); | ||
} ); | ||
// The fallback URL suggestion should not be shown when input is not URL-like. | ||
expect( | ||
searchResultElements[ searchResultElements.length - 1 ] | ||
).not.toHaveTextContent( 'URL' ); | ||
} | ||
); | ||
|
||
it.each( [ | ||
[ 'https://wordpress.org', 'URL' ], | ||
[ 'http://wordpress.org', 'URL' ], | ||
[ 'www.wordpress.org', 'URL' ], | ||
[ 'wordpress.org', 'URL' ], | ||
[ 'ftp://wordpress.org', 'URL' ], | ||
[ 'mailto:[email protected]', 'mailto' ], | ||
[ 'tel:123456789', 'tel' ], | ||
[ '#internal', 'internal' ], | ||
] )( | ||
'should display only URL result when current input value is URL-like (e.g. %s)', | ||
async ( searchTerm, type ) => { | ||
const user = userEvent.setup(); | ||
|
||
render( <LinkControl /> ); | ||
|
||
// Search Input UI. | ||
const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); | ||
|
||
// Simulate searching for a term. | ||
await user.type( searchInput, searchTerm ); | ||
|
||
const searchResultElement = within( | ||
await screen.findByRole( 'listbox', { | ||
name: /Search results for.*/, | ||
} ) | ||
).getByRole( 'option' ); | ||
|
||
expect( searchResultElement ).toBeInTheDocument(); | ||
|
||
// Should only be the `URL` suggestion. | ||
expect( searchInput ).toHaveAttribute( 'aria-expanded', 'true' ); | ||
|
||
expect( searchResultElement ).toHaveTextContent( searchTerm ); | ||
expect( searchResultElement ).toHaveTextContent( type ); | ||
expect( searchResultElement ).toHaveTextContent( | ||
'Press ENTER to add this link' | ||
); | ||
} | ||
); | ||
|
||
it( 'should trim search term', async () => { | ||
const user = userEvent.setup(); | ||
|
@@ -504,8 +546,7 @@ describe( 'Searching for a link', () => { | |
.flat() | ||
.filter( Boolean ); | ||
|
||
// Given we're mocking out the results we should always have 4 mark elements. | ||
expect( searchResultTextHighlightElements ).toHaveLength( 4 ); | ||
expect( searchResultTextHighlightElements ).toHaveLength( 3 ); | ||
|
||
// Make sure there are no `mark` elements which contain anything other | ||
// than the trimmed search term (ie: no whitespace). | ||
|
@@ -565,16 +606,15 @@ describe( 'Searching for a link', () => { | |
const lastSearchResultItem = | ||
searchResultElements[ searchResultElements.length - 1 ]; | ||
|
||
// We should see a search result for each of the expect search suggestions | ||
// plus 1 additional one for the fallback URL suggestion. | ||
// We should see a search result for each of the expect search suggestions. | ||
expect( searchResultElements ).toHaveLength( | ||
fauxEntitySuggestions.length + 1 | ||
fauxEntitySuggestions.length | ||
); | ||
|
||
// The last item should be a URL search suggestion. | ||
expect( lastSearchResultItem ).toHaveTextContent( searchTerm ); | ||
expect( lastSearchResultItem ).toHaveTextContent( 'URL' ); | ||
expect( lastSearchResultItem ).toHaveTextContent( | ||
// The URL search suggestion should not exist. | ||
expect( lastSearchResultItem ).not.toHaveTextContent( searchTerm ); | ||
expect( lastSearchResultItem ).not.toHaveTextContent( 'URL' ); | ||
expect( lastSearchResultItem ).not.toHaveTextContent( | ||
'Press ENTER to add this link' | ||
); | ||
} | ||
|
@@ -964,8 +1004,7 @@ describe( 'Default search suggestions', () => { | |
} ) | ||
).getAllByRole( 'option' ); | ||
|
||
// It should match any url that's like ?p= and also include a URL option. | ||
expect( searchResultElements ).toHaveLength( 5 ); | ||
expect( searchResultElements ).toHaveLength( 4 ); | ||
|
||
expect( searchInput ).toHaveAttribute( 'aria-expanded', 'true' ); | ||
|
||
|
66 changes: 66 additions & 0 deletions
66
packages/block-editor/src/components/link-control/test/is-url-like.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import isURLLike from '../is-url-like'; | ||
|
||
describe( 'isURLLike', () => { | ||
it.each( [ 'https://wordpress.org', 'http://wordpress.org' ] )( | ||
'returns true for a string that starts with an http(s) protocol', | ||
( testString ) => { | ||
expect( isURLLike( testString ) ).toBe( true ); | ||
} | ||
); | ||
|
||
it.each( [ | ||
'hello world', | ||
'https:// has spaces even though starts with protocol', | ||
'www. wordpress . org', | ||
] )( | ||
'returns false for any string with spaces (e.g. "%s")', | ||
( testString ) => { | ||
expect( isURLLike( testString ) ).toBe( false ); | ||
} | ||
); | ||
|
||
it( 'returns false for a string without a protocol or a TLD', () => { | ||
expect( isURLLike( 'somedirectentryhere' ) ).toBe( false ); | ||
} ); | ||
|
||
it( 'returns true for a string beginning with www.', () => { | ||
expect( isURLLike( 'www.wordpress.org' ) ).toBe( true ); | ||
} ); | ||
|
||
it.each( [ 'mailto:[email protected]', 'tel:123456' ] )( | ||
'returns true for common protocols', | ||
( testString ) => { | ||
expect( isURLLike( testString ) ).toBe( true ); | ||
} | ||
); | ||
|
||
it( 'returns true for internal anchor ("hash") links.', () => { | ||
expect( isURLLike( '#someinternallink' ) ).toBe( true ); | ||
} ); | ||
|
||
// use .each to test multiple cases | ||
it.each( [ | ||
[ true, 'http://example.com' ], | ||
[ true, 'https://test.co.uk?query=param' ], | ||
[ true, 'ftp://openai.ai?param=value#section' ], | ||
[ true, 'example.com' ], | ||
[ true, 'http://example.com?query=param#section' ], | ||
[ true, 'https://test.co.uk/some/path' ], | ||
[ true, 'ftp://openai.ai/some/path' ], | ||
[ true, 'example.org/some/path' ], | ||
[ true, 'example_test.tld' ], | ||
[ true, 'example_test.com' ], | ||
[ false, 'example' ], | ||
[ false, '.com' ], | ||
[ true, '_test.com' ], | ||
[ true, 'http://example_test.com' ], | ||
] )( | ||
'returns %s when testing against string "%s" for a valid TLD', | ||
( expected, testString ) => { | ||
expect( isURLLike( testString ) ).toBe( expected ); | ||
} | ||
); | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters