1- import { test , expect , Page } from '@playwright/test' ;
2-
3- // Helper functions
4- async function openLocalhost ( page : Page , port : number ) {
5- await page . goto ( `http://localhost:${ port } ` ) ;
6- await page . waitForLoadState ( 'networkidle' ) ;
7- }
8-
9- async function checkElementWithTextPresence ( page : Page , selector : string , text : string ) {
10- const element = page . locator ( `${ selector } :has-text("${ text } ")` ) ;
11- await expect ( element ) . toBeVisible ( ) ;
12- }
13-
14- async function clickElementWithText ( page : Page , selector : string , text : string ) {
15- await page . click ( `${ selector } :has-text("${ text } ")` ) ;
16- }
17-
18-
19-
20- const appsData = [
1+ import { test , expect } from '@playwright/test' ;
2+ import { BasePage } from './utils/base-test' ;
3+ import { Constants } from './utils/constants' ;
4+ import { selectors } from './utils/selectors' ;
5+
6+ type AppInfo = {
7+ host : number ;
8+ appDisplayName : string ;
9+ localButtonText : string ;
10+ localButtonColor : string ;
11+ remoteButtonText : string ;
12+ remoteButtonColor : string ;
13+ } ;
14+
15+ const headerText = Constants . commonConstantsData . headerText ;
16+
17+ const apps : AppInfo [ ] = [
2118 {
22- headerText : 'Module Federation with Automatic Vendor Sharing' ,
23- appNameText : 'App 1 (Host & Remote)' ,
24- buttonColor : 'rgb(255, 0, 0)' ,
2519 host : 3001 ,
20+ appDisplayName : Constants . commonConstantsData . appDisplayNames . app1 ,
21+ localButtonText : Constants . commonConstantsData . buttonLabels . app1 ,
22+ localButtonColor : Constants . color . app1Button ,
23+ remoteButtonText : Constants . commonConstantsData . buttonLabels . app2 ,
24+ remoteButtonColor : Constants . color . app2Button ,
2625 } ,
2726 {
28- headerText : 'Module Federation with Automatic Vendor Sharing' ,
29- appNameText : 'App 2 (Host & Remote)' ,
30- buttonColor : 'rgb(0, 0, 139)' ,
3127 host : 3002 ,
28+ appDisplayName : Constants . commonConstantsData . appDisplayNames . app2 ,
29+ localButtonText : Constants . commonConstantsData . buttonLabels . app2 ,
30+ localButtonColor : Constants . color . app2Button ,
31+ remoteButtonText : Constants . commonConstantsData . buttonLabels . app1 ,
32+ remoteButtonColor : Constants . color . app1Button ,
3233 } ,
3334] ;
3435
35- test . describe ( 'Automatic Vendor Sharing E2E Tests' , ( ) => {
36-
37- appsData . forEach ( ( appData ) => {
38- const { host, appNameText, headerText } = appData ;
39-
40- test . describe ( `Check ${ appNameText } ` , ( ) => {
41- test ( `should display ${ appNameText } header and subheader correctly` , async ( { page } ) => {
36+ test . describe ( 'Automatic Vendor Sharing example' , ( ) => {
37+ for ( const app of apps ) {
38+ test . describe ( app . appDisplayName , ( ) => {
39+ test ( `renders the shell for ${ app . appDisplayName } ` , async ( { page } ) => {
40+ const basePage = new BasePage ( page ) ;
4241 const consoleErrors : string [ ] = [ ] ;
42+
4343 page . on ( 'console' , ( msg ) => {
4444 if ( msg . type ( ) === 'error' ) {
4545 consoleErrors . push ( msg . text ( ) ) ;
4646 }
4747 } ) ;
4848
49- await openLocalhost ( page , host ) ;
50-
51- // Check header and subheader exist
52- await checkElementWithTextPresence ( page , 'h1' , headerText ) ;
53- await checkElementWithTextPresence ( page , 'h2' , appNameText ) ;
49+ await basePage . openLocalhost ( app . host ) ;
5450
55- // Verify no critical console errors
56- const criticalErrors = consoleErrors . filter ( error =>
57- error . includes ( 'Failed to fetch' ) ||
58- error . includes ( 'ChunkLoadError' ) ||
59- error . includes ( 'Module not found' ) ||
60- error . includes ( 'TypeError' )
61- ) ;
62- expect ( criticalErrors ) . toHaveLength ( 0 ) ;
63- } ) ;
51+ await expect ( page . locator ( selectors . tags . headers . h1 ) ) . toContainText ( headerText ) ;
52+ await expect ( page . locator ( selectors . tags . headers . h2 ) ) . toContainText ( app . appDisplayName ) ;
6453
65- test ( `should display ${ appNameText } button correctly` , async ( { page } ) => {
66- await openLocalhost ( page , host ) ;
54+ const relevantErrors = consoleErrors . filter ( ( error ) => {
55+ if ( error . includes ( 'WebSocket connection to' ) && error . includes ( 'WEB_SOCKET_CONNECT_MAGIC_ID' ) ) {
56+ return false ;
57+ }
6758
68- const buttonText = `${ appNameText . split ( ' ' ) [ 0 ] } ${ appNameText . split ( ' ' ) [ 1 ] } Button` ;
69-
70- // Check button exists with correct text
71- await checkElementWithTextPresence ( page , 'button' , buttonText ) ;
72- } ) ;
59+ if ( error . includes ( 'dynamic-remote-type-hints-plugin' ) ) {
60+ return false ;
61+ }
7362
74- test ( `should handle ${ appNameText } button interactions` , async ( { page } ) => {
75- await openLocalhost ( page , host ) ;
63+ return true ;
64+ } ) ;
7665
77- const buttonText = `${ appNameText . split ( ' ' ) [ 0 ] } ${ appNameText . split ( ' ' ) [ 1 ] } Button` ;
78-
79- // Click the button and verify it responds
80- await clickElementWithText ( page , 'button' , buttonText ) ;
81-
82- // Verify button is still visible and functional after click
83- await checkElementWithTextPresence ( page , 'button' , buttonText ) ;
84- } ) ;
85- } ) ;
86- } ) ;
87-
88- test . describe ( 'Cross-App Integration Tests' , ( ) => {
89- test ( 'should demonstrate automatic vendor sharing between apps' , async ( { page } ) => {
90- const networkRequests : string [ ] = [ ] ;
91-
92- page . on ( 'request' , ( request ) => {
93- networkRequests . push ( request . url ( ) ) ;
66+ expect ( relevantErrors , 'Unexpected console errors detected in the browser console' ) . toHaveLength ( 0 ) ;
9467 } ) ;
9568
96- // Visit both apps to trigger vendor sharing
97- await page . goto ( 'http://localhost:3001' ) ;
98- await page . waitForLoadState ( 'networkidle' ) ;
99-
100- await page . goto ( 'http://localhost:3002' ) ;
101- await page . waitForLoadState ( 'networkidle' ) ;
102-
103- // Verify shared dependencies are loaded efficiently
104- const reactRequests = networkRequests . filter ( url =>
105- url . includes ( 'react' ) && ! url . includes ( 'react-dom' )
106- ) ;
107-
108- // Should not load React multiple times due to vendor sharing
109- expect ( reactRequests . length ) . toBeLessThanOrEqual ( 10 ) ;
110- } ) ;
69+ test ( `exposes the styled local button for ${ app . appDisplayName } ` , async ( { page } ) => {
70+ const basePage = new BasePage ( page ) ;
11171
112- test ( 'should handle CORS correctly for federated modules' , async ( { page } ) => {
113- const corsErrors : string [ ] = [ ] ;
114- page . on ( 'response' , ( response ) => {
115- if ( response . status ( ) >= 400 && response . url ( ) . includes ( 'localhost:300' ) ) {
116- corsErrors . push ( `${ response . status ( ) } - ${ response . url ( ) } ` ) ;
117- }
118- } ) ;
72+ await basePage . openLocalhost ( app . host ) ;
11973
120- // Test cross-origin requests work properly
121- await page . goto ( 'http://localhost:3001' ) ;
122- await page . waitForLoadState ( 'networkidle' ) ;
74+ const localButton = page . getByRole ( 'button' , { name : app . localButtonText } ) ;
75+ await expect ( localButton ) . toBeVisible ( ) ;
76+ await expect ( localButton ) . toHaveCSS ( 'background-color' , app . localButtonColor ) ;
12377
124- // Should have no CORS errors
125- expect ( corsErrors ) . toHaveLength ( 0 ) ;
126- } ) ;
78+ await localButton . click ( ) ;
79+ await expect ( localButton ) . toBeVisible ( ) ;
80+ } ) ;
12781
128- test ( 'should load applications within reasonable time' , async ( { page } ) => {
129- const startTime = Date . now ( ) ;
130-
131- await page . goto ( 'http://localhost:3001' ) ;
132- await page . waitForLoadState ( 'networkidle' ) ;
133-
134- const loadTime = Date . now ( ) - startTime ;
135- expect ( loadTime ) . toBeLessThan ( 10000 ) ; // Should load within 10 seconds
136- } ) ;
137- } ) ;
82+ test ( `loads the remote button for ${ app . appDisplayName } ` , async ( { page } ) => {
83+ const basePage = new BasePage ( page ) ;
13884
139- test . describe ( 'AutomaticVendorFederation Features' , ( ) => {
140- test ( 'should demonstrate shared vendor optimization' , async ( { page } ) => {
141- await page . goto ( 'http://localhost:3001' ) ;
142- await page . waitForLoadState ( 'networkidle' ) ;
85+ await basePage . openLocalhost ( app . host ) ;
86+ await basePage . waitForDynamicImport ( ) ;
14387
144- // Check that the main elements are present
145- await checkElementWithTextPresence ( page , 'h1' , 'Module Federation with Automatic Vendor Sharing' ) ;
146- await checkElementWithTextPresence ( page , 'h2' , 'App 1 (Host & Remote)' ) ;
147- } ) ;
88+ const remoteButton = page . getByRole ( 'button' , { name : app . remoteButtonText } ) ;
89+ await expect ( remoteButton ) . toBeVisible ( ) ;
90+ await expect ( remoteButton ) . toHaveCSS ( 'background-color' , app . remoteButtonColor ) ;
14891
149- test ( 'should handle error boundaries correctly' , async ( { page } ) => {
150- const consoleErrors : string [ ] = [ ] ;
151- page . on ( 'console' , ( msg ) => {
152- if ( msg . type ( ) === 'error' ) {
153- consoleErrors . push ( msg . text ( ) ) ;
154- }
92+ await remoteButton . click ( ) ;
93+ await expect ( remoteButton ) . toBeVisible ( ) ;
15594 } ) ;
156-
157- await page . goto ( 'http://localhost:3001' ) ;
158- await page . waitForLoadState ( 'networkidle' ) ;
159-
160- // Click button to test functionality
161- const buttonExists = await page . locator ( 'button' ) . first ( ) . isVisible ( ) ;
162- if ( buttonExists ) {
163- await page . locator ( 'button' ) . first ( ) . click ( ) ;
164- await page . waitForTimeout ( 1000 ) ;
165- }
166-
167- // Should handle any errors gracefully
168- const criticalErrors = consoleErrors . filter ( error =>
169- error . includes ( 'Uncaught' ) &&
170- ! error . includes ( 'webpack-dev-server' ) &&
171- ! error . includes ( 'DevTools' )
172- ) ;
173- expect ( criticalErrors ) . toHaveLength ( 0 ) ;
17495 } ) ;
175- } ) ;
176- } ) ;
96+ }
97+ } ) ;
0 commit comments