UNPKG

@wordpress/block-library

Version:
297 lines (247 loc) 7.77 kB
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/react'; import { Controls } from '../controls'; import { useEntityBinding } from '../use-entity-binding'; import { useIsInvalidLink } from '../use-is-invalid-link'; globalThis.wpVitest.mockMatchMedia(); // Mock the updateAttributes function let mockUpdateAttributes; vi.mock( import( '../update-attributes' ), () => ( { updateAttributes: ( ...args ) => mockUpdateAttributes( ...args ), } ) ); // Mock the useToolsPanelDropdownMenuProps hook vi.mock( import( '../../../utils/hooks' ), () => ( { useToolsPanelDropdownMenuProps: () => ( {} ), } ) ); // Mock the useEntityBinding hook vi.mock( import( '../use-entity-binding' ), () => ( { useEntityBinding: vi.fn( () => ( { hasUrlBinding: false, isBoundEntityAvailable: false, clearBinding: vi.fn(), } ) ), } ) ); // Mock the useIsInvalidLink hook vi.mock( import( '../use-is-invalid-link' ), () => ( { useIsInvalidLink: vi.fn( () => [ false, false ] ), } ) ); describe( 'Controls', () => { // Initialize the mock function beforeAll( () => { mockUpdateAttributes = vi.fn(); } ); const defaultProps = { attributes: { label: 'Test Link', url: 'https://example.com', description: 'Test description', rel: 'nofollow', opensInNewTab: false, }, setAttributes: vi.fn(), setIsEditingControl: vi.fn(), clientId: 'test-client-id', }; beforeEach( () => { vi.clearAllMocks(); mockUpdateAttributes.mockClear(); // Reset useEntityBinding mock to default useEntityBinding.mockReturnValue( { hasUrlBinding: false, isBoundEntityAvailable: false, clearBinding: vi.fn(), } ); // Reset useIsInvalidLink mock to default useIsInvalidLink.mockReturnValue( [ false, false ] ); } ); it( 'renders all form controls', () => { render( <Controls { ...defaultProps } /> ); expect( screen.getByLabelText( 'Text' ) ).toBeInTheDocument(); expect( screen.getByText( 'Link to' ) ).toBeInTheDocument(); expect( screen.getByLabelText( 'Open in new tab' ) ).toBeInTheDocument(); expect( screen.getByLabelText( 'Description' ) ).toBeInTheDocument(); expect( screen.getByLabelText( 'Rel attribute' ) ).toBeInTheDocument(); } ); it( 'strips HTML from label values', () => { const propsWithHtml = { ...defaultProps, attributes: { ...defaultProps.attributes, label: '<strong>Bold Text</strong>', }, }; render( <Controls { ...propsWithHtml } /> ); const textInput = screen.getByLabelText( 'Text' ); expect( textInput.value ).toBe( 'Bold Text' ); } ); it( 'handles all form field changes correctly', () => { render( <Controls { ...defaultProps } /> ); // Test text change const textInput = screen.getByLabelText( 'Text' ); fireEvent.change( textInput, { target: { value: 'New Label' } } ); expect( defaultProps.setAttributes ).toHaveBeenCalledWith( { label: 'New Label', } ); // Test description change const descriptionInput = screen.getByLabelText( 'Description' ); fireEvent.change( descriptionInput, { target: { value: 'New Description' }, } ); expect( defaultProps.setAttributes ).toHaveBeenCalledWith( { description: 'New Description', } ); // Test rel change const relInput = screen.getByLabelText( 'Rel attribute' ); fireEvent.change( relInput, { target: { value: 'nofollow noopener' }, } ); expect( defaultProps.setAttributes ).toHaveBeenCalledWith( { rel: 'nofollow noopener', } ); // Test checkbox change const checkbox = screen.getByLabelText( 'Open in new tab' ); fireEvent.click( checkbox ); expect( defaultProps.setAttributes ).toHaveBeenCalledWith( { opensInNewTab: true, } ); } ); describe( 'URL binding help text', () => { it( 'shows invalid link help text when bound entity is not available', () => { useEntityBinding.mockReturnValue( { hasUrlBinding: true, isBoundEntityAvailable: false, clearBinding: vi.fn(), } ); const propsWithCategoryBinding = { ...defaultProps, attributes: { ...defaultProps.attributes, type: 'category', kind: 'taxonomy', }, }; render( <Controls { ...propsWithCategoryBinding } /> ); expect( screen.getByText( 'This link is invalid and will not appear on your site. Please update the link.' ) ).toBeInTheDocument(); } ); it( 'shows draft help text for draft entities', () => { useIsInvalidLink.mockReturnValue( [ false, true ] ); // isInvalid: false, isDraft: true const propsWithDraftPage = { ...defaultProps, attributes: { ...defaultProps.attributes, type: 'page', kind: 'post-type', }, }; render( <Controls { ...propsWithDraftPage } /> ); expect( screen.getByText( 'This link is to a draft page and will not appear on your site until the page is published.' ) ).toBeInTheDocument(); } ); it( 'shows draft help text for different entity types', () => { useIsInvalidLink.mockReturnValue( [ false, true ] ); const propsWithDraftPost = { ...defaultProps, attributes: { ...defaultProps.attributes, type: 'post', kind: 'post-type', }, }; render( <Controls { ...propsWithDraftPost } /> ); expect( screen.getByText( 'This link is to a draft post and will not appear on your site until the post is published.' ) ).toBeInTheDocument(); } ); it( 'does not show help text for valid link', () => { const propsWithValidLink = { ...defaultProps, attributes: { ...defaultProps.attributes, url: 'https://example.com', type: 'page', kind: 'post-type', }, }; render( <Controls { ...propsWithValidLink } /> ); // When link is valid (not invalid, not draft, no binding issues), no help text should be shown expect( screen.queryByText( /This link/ ) ).not.toBeInTheDocument(); } ); } ); describe( 'View button', () => { it( 'shows "View link" for external URLs', () => { const propsWithExternalLink = { ...defaultProps, attributes: { ...defaultProps.attributes, url: 'https://example.com', // No kind or type for external links }, }; render( <Controls { ...propsWithExternalLink } /> ); expect( screen.getByText( 'View' ) ).toBeVisible(); } ); it( 'shows "View page" for page links', () => { const propsWithPageLink = { ...defaultProps, attributes: { ...defaultProps.attributes, url: '/about', type: 'page', kind: 'post-type', }, }; render( <Controls { ...propsWithPageLink } /> ); expect( screen.getByText( 'View' ) ).toBeVisible(); } ); it( 'shows "View post" for post links', () => { const propsWithPostLink = { ...defaultProps, attributes: { ...defaultProps.attributes, url: '/blog/my-post', type: 'post', kind: 'post-type', }, }; render( <Controls { ...propsWithPostLink } /> ); expect( screen.getByText( 'View' ) ).toBeVisible(); } ); it( 'shows "View category" for category links', () => { const propsWithCategoryLink = { ...defaultProps, attributes: { ...defaultProps.attributes, url: '/category/tech', type: 'category', kind: 'taxonomy', }, }; render( <Controls { ...propsWithCategoryLink } /> ); expect( screen.getByText( 'View' ) ).toBeVisible(); } ); it( 'shows "View link" for custom type links', () => { const propsWithCustomLink = { ...defaultProps, attributes: { ...defaultProps.attributes, url: 'https://example.com', type: 'custom', kind: 'custom', }, }; render( <Controls { ...propsWithCustomLink } /> ); expect( screen.getByText( 'View' ) ).toBeVisible(); } ); } ); } );