@@ -806,6 +806,69 @@ describe('Tabs', () => {
806806 expect ( panelEl . getAttribute ( 'aria-labelledby' ) ) . toBe ( 'custom-tab-id' ) ;
807807 } ) ;
808808 } ) ;
809+
810+ describe ( 'structural validations' , ( ) => {
811+ let consoleSpy : jasmine . Spy ;
812+
813+ beforeEach ( ( ) => {
814+ consoleSpy = spyOn ( console , 'error' ) ;
815+ } ) ;
816+
817+ afterEach ( ( ) => {
818+ TestBed . resetTestingModule ( ) ;
819+ setupTestTabs ( ) ;
820+ } ) ;
821+
822+ it ( 'should warn when ngTab is missing its corresponding ngTabPanel' , ( ) => {
823+ TestBed . resetTestingModule ( ) ;
824+ TestBed . configureTestingModule ( {
825+ imports : [ TabWithoutPanelComponent ] ,
826+ } ) ;
827+ const noPanelFixture = TestBed . createComponent ( TabWithoutPanelComponent ) ;
828+ noPanelFixture . detectChanges ( ) ;
829+
830+ expect ( consoleSpy ) . toHaveBeenCalledWith (
831+ "ngTab with value 'tab1' does not have a corresponding ngTabPanel." ,
832+ ) ;
833+ } ) ;
834+
835+ it ( 'should warn when ngTabPanel is missing its corresponding ngTab' , ( ) => {
836+ TestBed . resetTestingModule ( ) ;
837+ TestBed . configureTestingModule ( {
838+ imports : [ PanelWithoutTabComponent ] ,
839+ } ) ;
840+ const noTabFixture = TestBed . createComponent ( PanelWithoutTabComponent ) ;
841+ noTabFixture . detectChanges ( ) ;
842+
843+ expect ( consoleSpy ) . toHaveBeenCalledWith (
844+ "ngTabPanel with value 'tab1' does not have a corresponding ngTab." ,
845+ ) ;
846+ } ) ;
847+
848+ it ( 'should warn when ngTabPanel is missing ngTabContent structural directive' , ( ) => {
849+ TestBed . resetTestingModule ( ) ;
850+ TestBed . configureTestingModule ( {
851+ imports : [ PanelWithoutContentComponent ] ,
852+ } ) ;
853+ const noContentFixture = TestBed . createComponent ( PanelWithoutContentComponent ) ;
854+ noContentFixture . detectChanges ( ) ;
855+
856+ expect ( consoleSpy ) . toHaveBeenCalledWith (
857+ 'ngTabPanel must have an ngTabContent structural directive to render.' ,
858+ ) ;
859+ } ) ;
860+
861+ it ( 'should warn when duplicate values are detected inside ngTabList' , ( ) => {
862+ TestBed . resetTestingModule ( ) ;
863+ TestBed . configureTestingModule ( {
864+ imports : [ DuplicateTabValuesComponent ] ,
865+ } ) ;
866+ const duplicateFixture = TestBed . createComponent ( DuplicateTabValuesComponent ) ;
867+ duplicateFixture . detectChanges ( ) ;
868+
869+ expect ( consoleSpy ) . toHaveBeenCalledWith ( "Duplicate value 'tab1' detected inside ngTabList." ) ;
870+ } ) ;
871+ } ) ;
809872} ) ;
810873
811874@Component ( {
@@ -882,3 +945,62 @@ class TestTabsComponent {
882945class TestTabsCustomIdComponent {
883946 selectedTab = signal ( 'tab1' ) ;
884947}
948+
949+ @Component ( {
950+ template : `
951+ <div ngTabs>
952+ <ul ngTabList>
953+ <li ngTab value="tab1">Tab 1</li>
954+ </ul>
955+ </div>
956+ ` ,
957+ imports : [ Tabs , TabList , Tab ] ,
958+ changeDetection : ChangeDetectionStrategy . Eager ,
959+ } )
960+ class TabWithoutPanelComponent { }
961+
962+ @Component ( {
963+ template : `
964+ <div ngTabs>
965+ <div ngTabPanel value="tab1">
966+ <ng-template ngTabContent>Content 1</ng-template>
967+ </div>
968+ </div>
969+ ` ,
970+ imports : [ Tabs , TabPanel , TabContent ] ,
971+ changeDetection : ChangeDetectionStrategy . Eager ,
972+ } )
973+ class PanelWithoutTabComponent { }
974+
975+ @Component ( {
976+ template : `
977+ <div ngTabs>
978+ <ul ngTabList>
979+ <li ngTab value="tab1">Tab 1</li>
980+ </ul>
981+ <div ngTabPanel value="tab1">
982+ Content 1
983+ </div>
984+ </div>
985+ ` ,
986+ imports : [ Tabs , TabList , Tab , TabPanel ] ,
987+ changeDetection : ChangeDetectionStrategy . Eager ,
988+ } )
989+ class PanelWithoutContentComponent { }
990+
991+ @Component ( {
992+ template : `
993+ <div ngTabs>
994+ <ul ngTabList>
995+ <li ngTab value="tab1">Tab 1</li>
996+ <li ngTab value="tab1">Tab 1 Copy</li>
997+ </ul>
998+ <div ngTabPanel value="tab1">
999+ <ng-template ngTabContent>Content 1</ng-template>
1000+ </div>
1001+ </div>
1002+ ` ,
1003+ imports : [ Tabs , TabList , Tab , TabPanel , TabContent ] ,
1004+ changeDetection : ChangeDetectionStrategy . Eager ,
1005+ } )
1006+ class DuplicateTabValuesComponent { }
0 commit comments