1+ import { test , expect } from '@playwright/test' ;
2+
3+ test . describe ( 'Issue #823: Tab ordering with ngIf/dynamic directives' , ( ) => {
4+ test . beforeEach ( async ( { page } ) => {
5+ await page . goto ( '/' ) ;
6+ await page . click ( 'text=Tabs' ) ;
7+ } ) ;
8+
9+ test ( 'should display tabs in correct order regardless of creation order' , async ( { page } ) => {
10+ // Wait for tabs to load
11+ await page . waitForSelector ( '.nav-tabs' ) ;
12+
13+ // Check that tabs exist and are in some order
14+ const tabs = page . locator ( '.nav-item' ) ;
15+ const tabCount = await tabs . count ( ) ;
16+
17+ if ( tabCount > 0 ) {
18+ // Tabs should be visible and clickable
19+ expect ( await tabs . first ( ) . isVisible ( ) ) . toBe ( true ) ;
20+
21+ // Should be able to click on tabs
22+ await tabs . first ( ) . click ( ) ;
23+
24+ // Active tab should have active class
25+ const activeTab = page . locator ( '.nav-item.active, .nav-link.active' ) ;
26+ expect ( await activeTab . count ( ) ) . toBeGreaterThan ( 0 ) ;
27+ }
28+ } ) ;
29+
30+ test ( 'should maintain tab functionality with ordered tabs' , async ( { page } ) => {
31+ // Test that tab ordering doesn't break basic functionality
32+ await page . waitForSelector ( '.nav-tabs' ) ;
33+
34+ const tabs = page . locator ( '.nav-link' ) ;
35+ const tabCount = await tabs . count ( ) ;
36+
37+ if ( tabCount >= 2 ) {
38+ // Click first tab
39+ await tabs . first ( ) . click ( ) ;
40+ expect ( await tabs . first ( ) . getAttribute ( 'class' ) ) . toContain ( 'active' ) ;
41+
42+ // Click second tab
43+ await tabs . nth ( 1 ) . click ( ) ;
44+ expect ( await tabs . nth ( 1 ) . getAttribute ( 'class' ) ) . toContain ( 'active' ) ;
45+
46+ // First tab should no longer be active
47+ expect ( await tabs . first ( ) . getAttribute ( 'class' ) ) . not . toContain ( 'active' ) ;
48+ }
49+ } ) ;
50+
51+ test ( 'should handle dynamic tab addition and removal' , async ( { page } ) => {
52+ // Test dynamic behavior that tabOrder should help with
53+ await page . waitForSelector ( '.nav-tabs' ) ;
54+
55+ const initialTabCount = await page . locator ( '.nav-item' ) . count ( ) ;
56+
57+ // Look for any buttons that might add/remove tabs dynamically
58+ const addButtons = page . locator ( 'button:has-text("Add"), button:has-text("New Tab")' ) ;
59+ const removeButtons = page . locator ( 'button:has-text("Remove"), .bs-remove-tab' ) ;
60+
61+ // If there are dynamic controls, test them
62+ if ( await addButtons . count ( ) > 0 ) {
63+ await addButtons . first ( ) . click ( ) ;
64+
65+ // Tab count might change
66+ const newTabCount = await page . locator ( '.nav-item' ) . count ( ) ;
67+ expect ( newTabCount ) . toBeGreaterThanOrEqual ( initialTabCount ) ;
68+ }
69+
70+ // Test removable tabs if they exist
71+ if ( await removeButtons . count ( ) > 0 ) {
72+ expect ( await removeButtons . first ( ) . isVisible ( ) ) . toBe ( true ) ;
73+ }
74+ } ) ;
75+
76+ test ( 'should preserve tab content when ordering changes' , async ( { page } ) => {
77+ // Ensure tab content is preserved with ordering
78+ await page . waitForSelector ( '.nav-tabs' ) ;
79+
80+ const tabs = page . locator ( '.nav-link' ) ;
81+ const tabContent = page . locator ( '.tab-content, .tab-pane' ) ;
82+
83+ if ( await tabs . count ( ) > 0 && await tabContent . count ( ) > 0 ) {
84+ // Click on different tabs and verify content shows
85+ for ( let i = 0 ; i < Math . min ( await tabs . count ( ) , 3 ) ; i ++ ) {
86+ await tabs . nth ( i ) . click ( ) ;
87+
88+ // Content should be visible
89+ expect ( await tabContent . first ( ) . isVisible ( ) ) . toBe ( true ) ;
90+ }
91+ }
92+ } ) ;
93+
94+ test ( 'should handle conditional tabs with ngIf-like behavior' , async ( { page } ) => {
95+ // Simulate the ngIf scenario from the issue
96+ await page . waitForSelector ( '.nav-tabs' ) ;
97+
98+ // Check if there are any conditional/dynamic elements
99+ const conditionalElements = page . locator ( '[*ngIf], [ng-if], .conditional-tab' ) ;
100+
101+ // Basic functionality should work even with conditional rendering
102+ const tabs = page . locator ( '.nav-link' ) ;
103+ if ( await tabs . count ( ) > 0 ) {
104+ // Should be able to navigate between tabs
105+ await tabs . first ( ) . click ( ) ;
106+
107+ // Tab should become active
108+ const activeClass = await tabs . first ( ) . getAttribute ( 'class' ) ;
109+ expect ( activeClass ) . toContain ( 'active' ) ;
110+ }
111+ } ) ;
112+
113+ test ( 'should maintain accessibility with ordered tabs' , async ( { page } ) => {
114+ // Test that tab ordering doesn't break accessibility
115+ await page . waitForSelector ( '.nav-tabs' ) ;
116+
117+ const tabs = page . locator ( '.nav-link' ) ;
118+
119+ if ( await tabs . count ( ) > 0 ) {
120+ // Check for proper ARIA attributes
121+ const firstTab = tabs . first ( ) ;
122+
123+ // Should have proper role
124+ const role = await firstTab . getAttribute ( 'role' ) ;
125+ expect ( role ) . toBe ( 'tab' ) ;
126+
127+ // Should have aria-selected
128+ const ariaSelected = await firstTab . getAttribute ( 'aria-selected' ) ;
129+ expect ( ariaSelected ) . toBeTruthy ( ) ;
130+
131+ // Should be focusable
132+ await firstTab . focus ( ) ;
133+ const focusedElement = page . locator ( ':focus' ) ;
134+ expect ( await focusedElement . count ( ) ) . toBe ( 1 ) ;
135+ }
136+ } ) ;
137+
138+ test ( 'should handle keyboard navigation with ordered tabs' , async ( { page } ) => {
139+ // Test keyboard navigation works with tab ordering
140+ await page . waitForSelector ( '.nav-tabs' ) ;
141+
142+ const tabs = page . locator ( '.nav-link' ) ;
143+
144+ if ( await tabs . count ( ) >= 2 ) {
145+ // Focus first tab
146+ await tabs . first ( ) . focus ( ) ;
147+
148+ // Use arrow keys to navigate
149+ await page . keyboard . press ( 'ArrowRight' ) ;
150+
151+ // Should move focus (implementation may vary)
152+ const focusedElement = page . locator ( ':focus' ) ;
153+ expect ( await focusedElement . count ( ) ) . toBe ( 1 ) ;
154+
155+ // Test Tab key navigation
156+ await page . keyboard . press ( 'Tab' ) ;
157+
158+ // Focus should move to next element
159+ expect ( await focusedElement . count ( ) ) . toBe ( 1 ) ;
160+ }
161+ } ) ;
162+
163+ test ( 'should handle edge cases for tab ordering' , async ( { page } ) => {
164+ // Test edge cases that might occur with tab ordering
165+ await page . waitForSelector ( '.nav-tabs' ) ;
166+
167+ const tabs = page . locator ( '.nav-item' ) ;
168+ const initialCount = await tabs . count ( ) ;
169+
170+ // Rapid clicking shouldn't break ordering
171+ if ( initialCount >= 2 ) {
172+ await tabs . first ( ) . click ( ) ;
173+ await tabs . nth ( 1 ) . click ( ) ;
174+ await tabs . first ( ) . click ( ) ;
175+
176+ // Should still work correctly
177+ expect ( await tabs . count ( ) ) . toBe ( initialCount ) ;
178+ }
179+
180+ // Page refresh should maintain consistent ordering
181+ await page . reload ( ) ;
182+ await page . waitForSelector ( '.nav-tabs' ) ;
183+
184+ const tabsAfterReload = page . locator ( '.nav-item' ) ;
185+ expect ( await tabsAfterReload . count ( ) ) . toBe ( initialCount ) ;
186+ } ) ;
187+
188+ test ( 'should support tab ordering in different layouts' , async ( { page } ) => {
189+ // Test that tab ordering works in different tab layouts
190+ await page . waitForSelector ( '.nav-tabs' ) ;
191+
192+ // Look for different tab styles/layouts
193+ const verticalTabs = page . locator ( '.nav-tabs.flex-column, .nav-pills.flex-column' ) ;
194+ const justifiedTabs = page . locator ( '.nav-justified' ) ;
195+ const pillTabs = page . locator ( '.nav-pills' ) ;
196+
197+ // Test basic functionality regardless of layout
198+ const anyTabs = page . locator ( '.nav-link' ) ;
199+
200+ if ( await anyTabs . count ( ) > 0 ) {
201+ // Should work in any layout
202+ await anyTabs . first ( ) . click ( ) ;
203+
204+ // Should have active state
205+ const activeTab = page . locator ( '.nav-link.active' ) ;
206+ expect ( await activeTab . count ( ) ) . toBe ( 1 ) ;
207+ }
208+ } ) ;
209+ } ) ;
0 commit comments