1111 * limitations under the License.
1212 */
1313import { expect } from '@playwright/test' ;
14+ import { ServiceTypes } from '../../constant/settings' ;
1415import { DataProduct } from '../../support/domain/DataProduct' ;
1516import { Domain } from '../../support/domain/Domain' ;
1617import { ApiEndpointClass } from '../../support/entity/ApiEndpointClass' ;
@@ -36,6 +37,19 @@ import {
3637} from '../../utils/entity' ;
3738import { test } from '../fixtures/pages' ;
3839
40+ // Service management pages render KnowledgePanel.DataProducts only inside the
41+ // entity-count tab (ServiceMainTabContent). This maps each ServiceTypes value
42+ // to the matching tab label so we can click it before assigning data products.
43+ const SERVICE_ENTITY_TAB : Partial < Record < ServiceTypes , string > > = {
44+ [ ServiceTypes . API_SERVICES ] : 'Collections' ,
45+ [ ServiceTypes . DASHBOARD_SERVICES ] : 'Dashboards' ,
46+ [ ServiceTypes . MESSAGING_SERVICES ] : 'Topics' ,
47+ [ ServiceTypes . PIPELINE_SERVICES ] : 'Pipelines' ,
48+ [ ServiceTypes . ML_MODEL_SERVICES ] : 'ML Models' ,
49+ [ ServiceTypes . STORAGE_SERVICES ] : 'Containers' ,
50+ [ ServiceTypes . SEARCH_SERVICES ] : 'Search Indexes' ,
51+ } ;
52+
3953let domain : Domain ;
4054let dataProduct : DataProduct ;
4155
@@ -68,7 +82,7 @@ entities.forEach((EntityClass) => {
6882 const entity = new EntityClass ( ) ;
6983
7084 test . describe ( entity . getType ( ) , ( ) => {
71- test . beforeAll ( 'setup entity' + entity . getType ( ) , async ( { browser } ) => {
85+ test . beforeAll ( 'setup entity ' + entity . getType ( ) , async ( { browser } ) => {
7286 const { afterAction, apiContext } = await performAdminLogin ( browser ) ;
7387 await entity . create ( apiContext ) ;
7488 await afterAction ( ) ;
@@ -85,39 +99,83 @@ entities.forEach((EntityClass) => {
8599
86100 await entity . visitEntityPage ( page ) ;
87101
102+ // Table and StoredProcedure have 3 breadcrumbs; clicking index 1 lands
103+ // on the Database entity page which already exposes KnowledgePanel.DataProducts.
104+ // All other entities have 1 breadcrumb (service) or their intermediate
105+ // breadcrumb resolves to the service management page.
106+ const isDbEntity = [ 'Table' , 'Store Procedure' ] . includes (
107+ entity . getType ( )
108+ ) ;
109+ const is3Breadcrumb = [
110+ 'Table' ,
111+ 'ApiEndpoint' ,
112+ 'Store Procedure' ,
113+ ] . includes ( entity . getType ( ) ) ;
114+
88115 await expect ( page . getByTestId ( 'breadcrumb-link' ) ) . toHaveCount (
89- [ 'Table' , 'ApiEndpoint' , 'Store Procedure' ] . includes ( entity . getType ( ) )
90- ? 3
91- : 1
116+ is3Breadcrumb ? 3 : 1
92117 ) ;
93118
94- // click database
119+ // Navigate to the parent and assign domain.
95120 await page
96121 . getByTestId ( 'breadcrumb-link' )
97- . nth (
98- [ 'Table' , 'ApiEndpoint' , 'Store Procedure' ] . includes ( entity . getType ( ) )
99- ? 1
100- : 0
101- )
122+ . nth ( is3Breadcrumb ? 1 : 0 )
102123 . click ( ) ;
103- // assign domain
124+
104125 await assignSingleSelectDomain ( page , domain . responseData ) ;
105126 await waitForAllLoadersToDisappear ( page ) ;
106127
128+ // For service management pages KnowledgePanel.DataProducts is rendered
129+ // inside ServiceMainTabContent, which lives in the entity-count tab
130+ // (e.g. "ML Models", "Dashboards"). Click it so the panel becomes visible.
131+ if ( ! isDbEntity && entity . serviceType ) {
132+ const tabLabel = SERVICE_ENTITY_TAB [ entity . serviceType ] ;
133+
134+ if ( tabLabel ) {
135+ await page
136+ . getByRole ( 'tab' , { name : new RegExp ( tabLabel , 'i' ) } )
137+ . click ( ) ;
138+ await waitForAllLoadersToDisappear ( page ) ;
139+ }
140+ }
141+
142+ // Assign data product to the parent so the entity inherits it.
143+ // Domain is directly assigned above, no polling needed.
144+ await assignDataProduct ( page , domain . responseData , [
145+ dataProduct . responseData ,
146+ ] ) ;
147+
148+ // Back to entity; poll until the inherited domain propagates via ES.
107149 await redirectToHomePage ( page ) ;
108-
109150 await entity . visitEntityPage ( page ) ;
110151
111- await assignDataProduct (
112- page ,
113- domain . responseData ,
114- [ dataProduct . responseData ] ,
115- 'Add' ,
116- 'KnowledgePanel.DataProducts' ,
117- true
118- ) ;
152+ await expect
153+ . poll (
154+ async ( ) => {
155+ const entityResponse = page . waitForResponse (
156+ ( r ) =>
157+ r . url ( ) . includes ( `/api/v1/${ entity . endpoint } /` ) &&
158+ r . status ( ) === 200
159+ ) ;
160+ await page . reload ( ) ;
161+ await entityResponse ;
162+ await waitForAllLoadersToDisappear ( page ) ;
163+
164+ return page
165+ . getByTestId ( 'domain-link' )
166+ . textContent ( )
167+ . catch ( ( ) => null ) ;
168+ } ,
169+ {
170+ message : `Waiting for inherited domain "${ domain . responseData . displayName } "` ,
171+ timeout : 120_000 ,
172+ intervals : [ 3_000 , 5_000 , 10_000 ] ,
173+ }
174+ )
175+ . toContain ( domain . responseData . displayName ) ;
119176
120- // This will delete and restore and ensure both operation are successful
177+ // Entity owns no domain or data products (both inherited from parent),
178+ // so the backend restore check passes.
121179 await softDeleteEntity (
122180 page ,
123181 entity . entityResponseData . name ,
0 commit comments