1- import { test , expect , Page } from '@playwright/test' ;
2-
3- // Helper functions
4- async function openLocalhost ( page : Page , port : number ) {
5- // Set up console and error logging
6- const consoleMessages : string [ ] = [ ] ;
7- const pageErrors : string [ ] = [ ] ;
8-
9- page . on ( 'console' , ( msg ) => {
10- consoleMessages . push ( `[${ msg . type ( ) } ] ${ msg . text ( ) } ` ) ;
11- } ) ;
12-
13- page . on ( 'pageerror' , ( err ) => {
14- pageErrors . push ( `Page error: ${ err . message } \nStack: ${ err . stack || 'No stack trace' } ` ) ;
15- } ) ;
1+ import { test , expect } from '@playwright/test' ;
2+ import { BasePage } from './utils/base-test' ;
3+ import { selectors } from './utils/selectors' ;
4+ import { Constants } from './utils/constants' ;
165
17- await page . goto ( `http://localhost:${ port } ` ) ;
18-
19- // Wait for the page to load but don't wait for networkidle since env loading might be polling
20- await page . waitForLoadState ( 'load' ) ;
21-
22- // Wait for the root element to be attached. It may not be visible immediately
23- // (for example, the remote app shows an empty #root while it loads env config),
24- // so we only wait for the element to exist in the DOM.
25- await page . waitForSelector ( '#root' , { state : 'attached' , timeout : 10000 } ) ;
26-
27- // Log any errors found
28- if ( pageErrors . length > 0 ) {
29- console . log ( '=== PAGE ERRORS ===' ) ;
30- pageErrors . forEach ( error => console . log ( error ) ) ;
31- console . log ( '==================' ) ;
32- }
33-
34- if ( consoleMessages . length > 0 ) {
35- console . log ( '=== CONSOLE MESSAGES ===' ) ;
36- consoleMessages . forEach ( msg => console . log ( msg ) ) ;
37- console . log ( '========================' ) ;
38- }
39- }
40-
41- async function waitForEnvironmentLoading ( page : Page ) {
42- // Wait for either the loading screen to disappear or main content to appear
43- // The loading screen shows "Loading environment configuration..."
44- const loadingText = page . locator ( 'text=Loading environment configuration...' ) ;
45- const mainContent = page . locator ( 'h1' ) ;
46-
47- try {
48- // Wait up to 15 seconds for either loading to finish or main content to appear
49- await Promise . race ( [
50- loadingText . waitFor ( { state : 'hidden' , timeout : 15000 } ) ,
51- mainContent . waitFor ( { state : 'visible' , timeout : 15000 } )
52- ] ) ;
53- } catch ( error ) {
54- console . log ( 'Environment loading timeout - checking current page state' ) ;
55- const pageContent = await page . content ( ) ;
56- console . log ( 'Current page content length:' , pageContent . length ) ;
57-
58- // If still loading, wait a bit more and proceed
59- if ( await loadingText . isVisible ( ) ) {
60- console . log ( 'Still showing loading screen, waiting 10 more seconds...' ) ;
61- await page . waitForTimeout ( 10000 ) ;
62- }
63- }
64- }
65-
66- async function checkElementWithTextPresence ( page : Page , selector : string , text : string ) {
67- const element = page . locator ( `${ selector } :has-text("${ text } ")` ) ;
68- await expect ( element ) . toBeVisible ( ) ;
69- }
70-
71- async function clickElementWithText ( page : Page , selector : string , text : string ) {
72- await page . click ( `${ selector } :has-text("${ text } ")` ) ;
73- }
74-
75- async function checkDateFormat ( page : Page ) {
76- const dateElement = page . locator ( 'text=/[A-Z][a-z]+ \\d{1,2}[a-z]{2} \\d{4}, \\d{1,2}:\\d{2}/' ) . first ( ) ;
77- await expect ( dateElement ) . toBeVisible ( ) ;
78- }
79-
80- const appsData = [
81- {
82- header : 'Dynamic Remotes with Runtime Environment Variables' ,
83- subheader : 'Host' ,
84- hostH3 : 'Environment Configuration:' ,
85- paragraph : 'This example demonstrates how Module Federation can load remote components dynamically' ,
86- button : 'Load Remote Widget' ,
87- buttonH2 : 'Remote Widget' ,
88- buttonParagraph : 'Using momentjs for format the date' ,
89- host : 3000 ,
90- } ,
91- {
92- header : 'Dynamic System Host' ,
93- subheader : 'Remote' ,
94- buttonH2 : 'Remote Widget' ,
95- buttonParagraph : 'Using momentjs for format the date' ,
96- host : 3001 ,
97- } ,
98- ] ;
99-
100- test . describe ( 'Dynamic Remotes Runtime Environment Variables E2E Tests' , ( ) => {
101-
102- appsData . forEach ( ( appData ) => {
103- const { host, subheader, header, hostH3, paragraph, button, buttonH2, buttonParagraph } = appData ;
104-
105- test . describe ( `Check ${ subheader } app` , ( ) => {
106- test ( `should display ${ subheader } app widget functionality and application elements` , async ( { page } ) => {
107- await openLocalhost ( page , host ) ;
108-
109- // Wait for environment loading to complete for host app
110- if ( host === 3000 ) {
111- await waitForEnvironmentLoading ( page ) ;
112- }
113-
114- // Check main header
115- await checkElementWithTextPresence ( page , 'h1' , header ) ;
116-
117- if ( host === 3000 ) {
118- // Host app specific elements
119- await checkElementWithTextPresence ( page , 'h3' , hostH3 ! ) ;
120- await checkElementWithTextPresence ( page , 'p' , paragraph ! ) ;
121-
122- // Click the load remote component button
123- await clickElementWithText ( page , 'button' , button ! ) ;
124-
125- // Wait for loading to complete
126- await page . waitForTimeout ( 3000 ) ;
127- }
128-
129- // Check that the remote component loaded successfully
130- await checkElementWithTextPresence ( page , 'h2' , buttonH2 ) ;
131- await checkElementWithTextPresence ( page , 'p' , buttonParagraph ) ;
132-
133- // Check moment.js date formatting
134- await checkDateFormat ( page ) ;
135- } ) ;
136- } ) ;
137- } ) ;
6+ const {
7+ host,
8+ remoteApp,
9+ widget,
10+ } = Constants . elementsText . dynamicSystemRemotesRuntimeApp ;
13811
139- test . describe ( 'Runtime Environment Variable Tests' , ( ) => {
140- test ( 'should load environment configuration successfully' , async ( { page } ) => {
141- const networkRequests : string [ ] = [ ] ;
142-
143- page . on ( 'request' , ( request ) => {
144- networkRequests . push ( request . url ( ) ) ;
145- } ) ;
146-
147- await page . goto ( 'http://localhost:3000' ) ;
148- await page . waitForLoadState ( 'load' ) ;
149- await waitForEnvironmentLoading ( page ) ;
150-
151- // Check that env-config.json was loaded
152- const envConfigRequests = networkRequests . filter ( url =>
153- url . includes ( 'env-config.json' )
154- ) ;
155-
156- expect ( envConfigRequests . length ) . toBeGreaterThan ( 0 ) ;
157- } ) ;
12+ const { envLoader, remoteConfigLoader } = Constants . commonConstantsData ;
13+
14+ test . describe ( 'Dynamic Remotes with runtime environment variables' , ( ) => {
15+ test ( 'host application loads the remote widget on demand' , async ( { page } ) => {
16+ const basePage = new BasePage ( page ) ;
17+
18+ await basePage . openLocalhost ( 3000 ) ;
19+
20+ await basePage . waitForTextToDisappear ( selectors . tags . coreElements . div , envLoader , 15000 ) ;
15821
159- test ( 'should demonstrate dynamic remote loading with environment config' , async ( { page } ) => {
160- await openLocalhost ( page , 3000 ) ;
161- await waitForEnvironmentLoading ( page ) ;
22+ await basePage . checkElementWithTextPresence ( selectors . tags . headers . h1 , host . header ) ;
23+ await basePage . checkElementWithTextPresence ( selectors . tags . headers . h3 , host . envSectionTitle ) ;
24+ await basePage . checkElementWithTextPresence ( selectors . tags . paragraph , host . paragraph ) ;
16225
163- // Click to load remote component
164- await clickElementWithText ( page , 'button' , 'Load Remote Widget' ) ;
26+ await basePage . clickElementWithText ( selectors . tags . coreElements . button , host . button ) ;
16527
166- // Wait for loading to complete
167- await page . waitForTimeout ( 3000 ) ;
28+ await basePage . waitForTextToDisappear ( selectors . tags . coreElements . div , host . remoteLoading , 15000 ) ;
29+ await basePage . waitForTextToDisappear ( selectors . tags . coreElements . div , remoteConfigLoader , 15000 ) ;
16830
169- // Verify remote component is now loaded
170- await checkElementWithTextPresence ( page , 'h2' , 'Remote Widget' ) ;
171- await checkElementWithTextPresence ( page , 'p' , 'Using momentjs for format the date' ) ;
31+ await basePage . checkElementWithTextPresence ( selectors . tags . headers . h3 , host . remoteSectionTitle ) ;
32+ await basePage . checkElementWithTextPresence ( selectors . tags . headers . h2 , widget . title ) ;
33+
34+ const envHeading = page . getByRole ( 'heading' , {
35+ level : 2 ,
36+ name : new RegExp ( `^${ widget . envPrefix } ` ) ,
17237 } ) ;
38+ await expect ( envHeading ) . toHaveText ( new RegExp ( `^${ widget . envPrefix } https?://` ) ) ;
39+
40+ await basePage . checkElementWithTextPresence ( selectors . tags . paragraph , widget . paragraph ) ;
41+ await basePage . checkDateFormat ( ) ;
17342 } ) ;
17443
175- test . describe ( 'Performance and Loading' , ( ) => {
176- test ( 'should load applications within reasonable time' , async ( { page } ) => {
177- const startTime = Date . now ( ) ;
178-
179- await page . goto ( 'http://localhost:3000' ) ;
180- await page . waitForLoadState ( 'load' ) ;
181- await waitForEnvironmentLoading ( page ) ;
182-
183- const loadTime = Date . now ( ) - startTime ;
184- expect ( loadTime ) . toBeLessThan ( 10000 ) ; // Should load within 10 seconds
44+ test ( 'remote application exposes the widget with runtime configuration' , async ( { page } ) => {
45+ const basePage = new BasePage ( page ) ;
46+
47+ await basePage . openLocalhost ( 3001 ) ;
48+
49+ await basePage . checkElementWithTextPresence ( selectors . tags . headers . h1 , remoteApp . header ) ;
50+ await expect (
51+ page . getByRole ( 'heading' , { level : 2 , name : new RegExp ( `^${ remoteApp . subheader } $` ) } ) ,
52+ ) . toBeVisible ( ) ;
53+
54+ await basePage . waitForTextToDisappear ( selectors . tags . coreElements . div , remoteConfigLoader , 15000 ) ;
55+
56+ await basePage . checkElementWithTextPresence ( selectors . tags . headers . h2 , widget . title ) ;
57+
58+ const remoteEnvHeading = page . getByRole ( 'heading' , {
59+ level : 2 ,
60+ name : new RegExp ( `^${ widget . envPrefix } ` ) ,
18561 } ) ;
62+ await expect ( remoteEnvHeading ) . toHaveText ( new RegExp ( `^${ widget . envPrefix } https?://` ) ) ;
63+
64+ await basePage . checkElementWithTextPresence ( selectors . tags . paragraph , widget . paragraph ) ;
65+ await basePage . checkDateFormat ( ) ;
18666 } ) ;
187- } ) ;
67+ } ) ;
0 commit comments