Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jest.mock( '../../../lock-unlock', () => ( {
import {
computeDisplayUrl,
computeBadges,
isHomepage,
useLinkPreview,
} from '../use-link-preview';

Expand Down Expand Up @@ -135,6 +136,74 @@ describe( 'computeDisplayUrl', () => {
} );
} );

describe( 'isHomepage', () => {
const host = 'homepage.com';
const homeUrl = 'https://' + host;

test.each( [
[ '/', homeUrl ],
[ '/', undefined ],
] )( 'should return true for root path "%s"', ( url, homeUrlParam ) => {
expect( isHomepage( url, homeUrlParam ) ).toBe( true );
} );

// Check combinations of http/s and trailing slash
test.each( [
[ `http://${ host }`, homeUrl ],
[ `https://${ host }`, homeUrl ],
[ `http://${ host }/`, homeUrl ],
[ `https://${ host }/`, homeUrl ],
] )( 'should return true for site URL "%s"', ( url ) => {
expect( isHomepage( url, homeUrl ) ).toBe( true );
} );

test.each( [
[ '', homeUrl ],
[ `https://${ host }`, '' ],
[ `https://${ host }`, undefined ],
] )(
'should return false when url or homeUrl is empty and not a / path',
( url, homeUrlParam ) => {
expect( isHomepage( url, homeUrlParam ) ).toBe( false );
}
);

const subdomain = 'sub.' + host;
test.each( [
[ false, `http://${ subdomain }/`, homeUrl ],
[ false, `https://${ subdomain }`, homeUrl ],
[ true, `http://${ subdomain }/`, `https://${ subdomain }` ],
[ true, `https://${ subdomain }`, `https://${ subdomain }` ],
] )(
'should return %s for subdomain (url=%s, homeUrl=%s)',
( expected, url, homeUrlParam ) => {
expect( isHomepage( url, homeUrlParam ) ).toBe( expected );
}
);

const path = '/wordpress';
const subdirHomeUrl = 'https://' + host + path;

test.each( [
[ `https://${ host }${ path }`, subdirHomeUrl ],
[ `https://${ host }${ path }/`, subdirHomeUrl ],
[ `http://${ host }${ path }`, subdirHomeUrl ],
[ `http://${ host }${ path }/`, subdirHomeUrl ],
] )( 'should return true for subdirectory homepage "%s"', ( url ) => {
expect( isHomepage( url, subdirHomeUrl ) ).toBe( true );
} );

test.each( [
[ `https://${ host }/page`, homeUrl ],
[ '/page', homeUrl ],
[ `https://${ host }`, subdirHomeUrl ],
[ `https://${ host }/`, subdirHomeUrl ],
[ `https://${ host }${ path }/page`, subdirHomeUrl ],
] )( 'should return false for non-homepage "%s"', ( url, homeUrlParam ) => {
expect( isHomepage( url, homeUrlParam ) ).toBe( false );
} );
} );

describe( 'computeBadges', () => {
describe( 'kind badges', () => {
it( 'should show "External link" badge for external links', () => {
Expand Down Expand Up @@ -174,6 +243,67 @@ describe( 'computeBadges', () => {
} );
} );

it( 'should show "Homepage" badge for root path', () => {
const badges = computeBadges( {
url: '/',
isExternal: false,
} );

expect( badges ).toContainEqual( {
label: 'Homepage',
intent: 'default',
} );
} );

test.each( [
[ 'https://example.com' ],
[ 'https://example.com/' ],
[ 'http://example.com' ],
[ 'http://example.com/' ],
] )( 'should show "Homepage" badge when url is "%s"', ( url ) => {
const badges = computeBadges( {
url,
homeUrl: 'https://example.com',
isExternal: false,
} );
expect( badges ).toContainEqual( {
label: 'Homepage',
intent: 'default',
} );
} );

test.each( [ [ 'https://sub.example.com/', 'https://example.com' ] ] )(
'should not show Homepage badge when subdomain url "%s" does not match homeUrl "%s"',
( url, homeUrl ) => {
const badges = computeBadges( {
url,
homeUrl,
isExternal: false,
} );
expect( badges ).not.toContainEqual( {
label: 'Homepage',
intent: 'default',
} );
}
);

test.each( [
[ 'https://sub.example.com/', 'https://sub.example.com' ],
] )(
'should show Homepage badge when subdomain url "%s" matches homeUrl "%s"',
( url, homeUrl ) => {
const badges = computeBadges( {
url,
homeUrl,
isExternal: false,
} );
expect( badges ).toContainEqual( {
label: 'Homepage',
intent: 'default',
} );
}
);

it( 'should show page badge for relative paths', () => {
const badges = computeBadges( {
url: '/relative-path',
Expand Down Expand Up @@ -260,6 +390,25 @@ it( 'should show "Internal link" badge for hash links even when type is present'
} );
} );

it( 'should show "Homepage" badge for root path even when type is present', () => {
const badges = computeBadges( {
url: '/',
type: 'page',
isExternal: false,
} );

// Should prioritize homepage detection over type
expect( badges ).toContainEqual( {
label: 'Homepage',
intent: 'default',
} );
// Should NOT show Page badge
expect( badges ).not.toContainEqual( {
label: 'Page',
intent: 'default',
} );
} );

test.each( [
[ 'www.test.com', 'URLs without protocol' ],
[ 'google.com', 'domain-only URLs without protocol' ],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,41 @@ function capitalize( str ) {
return str.charAt( 0 ).toUpperCase() + str.slice( 1 );
}

/**
* Check if a URL points to the site homepage.
* Handles protocol (http/https) and trailing slash variations.
* Does not match subdomains unless they are the site URL.
*
* @param {string} url - The URL to check
* @param {string} homeUrl - The WordPress site URL
* @return {boolean} True if url is the homepage
*/
export function isHomepage( url, homeUrl ) {
if ( url === '/' ) {
return true;
}
if ( ! url || ! homeUrl ) {
return false;
}
try {
const urlParsed = new URL( url, homeUrl );
const homeParsed = new URL( homeUrl );

// Same host, i.e. sub.homepage.com or homepage.com
if ( urlParsed.hostname !== homeParsed.hostname ) {
return false;
}

// Path must match site root (normalize trailing slash)
const urlPath = urlParsed.pathname.replace( /\/$/, '' );
const homePath = homeParsed.pathname.replace( /\/$/, '' );

return urlPath === homePath;
} catch {
return false;
}
}

/**
* Compute display URL - strips site URL if internal, shows full URL if external.
*
Expand Down Expand Up @@ -82,6 +117,7 @@ export function computeDisplayUrl( { linkUrl, homeUrl } = {} ) {
*
* @param {Object} options - Options object
* @param {string} options.url - Link URL
* @param {string} options.homeUrl - WordPress site URL (for homepage detection)
* @param {string} options.type - Entity type (page, post, etc.)
* @param {boolean} options.isExternal - Whether link is external
* @param {string} options.entityStatus - Entity status (publish, draft, etc.)
Expand All @@ -91,14 +127,14 @@ export function computeDisplayUrl( { linkUrl, homeUrl } = {} ) {
*/
export function computeBadges( {
url,
homeUrl,
type,
isExternal,
entityStatus,
hasBinding,
isEntityAvailable,
} ) {
const badges = [];

// Kind badge
if ( url ) {
if ( isExternal ) {
Expand All @@ -113,6 +149,11 @@ export function computeBadges( {
label: __( 'Internal link' ),
intent: 'default',
} );
} else if ( isHomepage( url, homeUrl ) ) {
badges.push( {
label: __( 'Homepage' ),
intent: 'default',
} );
} else if ( type && type !== 'custom' ) {
// Show entity type badge (page, post, category, etc.)
// but not 'custom' since that's just a manual link
Expand Down Expand Up @@ -229,6 +270,7 @@ export function useLinkPreview( {
// Compute badges
const badges = computeBadges( {
url,
homeUrl,
type,
isExternal,
entityStatus: entityRecord?.status,
Expand Down
Loading