@@ -14,167 +14,202 @@ let bsLocal;
1414
1515// Helper to start a local HTTP server
1616function startHttpServer ( port , rootDir ) {
17- return new Promise ( ( resolve , reject ) => {
18- server = http . createServer ( ( req , res ) => {
19- handler ( req , res , {
20- public : rootDir ,
21- cleanUrls : false ,
22- } ) ;
17+ return new Promise ( ( resolve , reject ) => {
18+ server = http . createServer ( ( req , res ) => {
19+ handler ( req , res , {
20+ public : rootDir ,
21+ cleanUrls : false ,
22+ } ) ;
23+ } ) ;
24+
25+ server . listen ( port , 'localhost' , ( ) => {
26+ console . log ( `Server running on http://localhost:${ port } ` ) ;
27+ resolve ( ) ;
28+ } ) ;
29+
30+ server . on ( 'error' , reject ) ;
2331 } ) ;
24-
25- server . listen ( port , 'localhost' , ( ) => {
26- console . log ( `Server running on http://localhost:${ port } ` ) ;
27- resolve ( ) ;
28- } ) ;
29-
30- server . on ( 'error' , reject ) ;
31- } ) ;
3232}
3333
3434// Helper to stop the HTTP server
3535function stopHttpServer ( ) {
36- return new Promise ( ( resolve , reject ) => {
37- if ( server ) {
38- server . close ( ( err ) => {
39- if ( err ) reject ( err ) ;
40- else resolve ( ) ;
41- } ) ;
42- } else {
43- resolve ( ) ;
44- }
45- } ) ;
36+ return new Promise ( ( resolve , reject ) => {
37+ if ( server ) {
38+ server . close ( ( err ) => {
39+ if ( err ) reject ( err ) ;
40+ else resolve ( ) ;
41+ } ) ;
42+ } else {
43+ resolve ( ) ;
44+ }
45+ } ) ;
4646}
4747
4848// Helper to start BrowserStack Local
4949function startBrowserStackLocal ( username , accessKey , localIdentifier ) {
50- return new Promise ( ( resolve , reject ) => {
51- bsLocal = new BrowserStackLocal . Local ( ) ;
52- const bsLocalArgs = {
53- key : accessKey ,
54- localIdentifier : localIdentifier ,
55- forceLocal : true ,
56- } ;
57-
58- bsLocal . start ( bsLocalArgs , ( err ) => {
59- if ( err ) {
60- reject ( new Error ( `Failed to start BrowserStack Local: ${ err } ` ) ) ;
61- } else {
62- console . log ( 'BrowserStack Local started' ) ;
63- resolve ( ) ;
64- }
50+ return new Promise ( ( resolve , reject ) => {
51+ bsLocal = new BrowserStackLocal . Local ( ) ;
52+ const bsLocalArgs = {
53+ key : accessKey ,
54+ localIdentifier : localIdentifier ,
55+ forceLocal : true ,
56+ } ;
57+
58+ bsLocal . start ( bsLocalArgs , ( err ) => {
59+ if ( err ) {
60+ reject ( new Error ( `Failed to start BrowserStack Local: ${ err } ` ) ) ;
61+ } else {
62+ console . log ( 'BrowserStack Local started' ) ;
63+ resolve ( ) ;
64+ }
65+ } ) ;
6566 } ) ;
66- } ) ;
6767}
6868
6969// Helper to stop BrowserStack Local
7070function stopBrowserStackLocal ( ) {
71- return new Promise ( ( resolve ) => {
72- if ( bsLocal && bsLocal . isRunning ( ) ) {
73- bsLocal . stop ( ( ) => {
74- console . log ( 'BrowserStack Local stopped' ) ;
75- resolve ( ) ;
76- } ) ;
77- } else {
78- resolve ( ) ;
79- }
80- } ) ;
71+ return new Promise ( ( resolve ) => {
72+ if ( bsLocal && bsLocal . isRunning ( ) ) {
73+ bsLocal . stop ( ( ) => {
74+ console . log ( 'BrowserStack Local stopped' ) ;
75+ resolve ( ) ;
76+ } ) ;
77+ } else {
78+ resolve ( ) ;
79+ }
80+ } ) ;
8181}
8282
8383test . describe ( 'BrowserStack Playwright Demo' , ( ) => {
84- const username = process . env . BROWSERSTACK_USERNAME ;
85- const accessKey = process . env . BROWSERSTACK_ACCESS_KEY ;
86- const localIdentifier = `local-${ Date . now ( ) } ` ;
87- const localPort = 8080 ;
88- const exampleDir = path . join ( repoRoot , 'packages' , 'plugin-button-click-counter' ) ;
89-
90- test . beforeAll ( async ( ) => {
91- if ( ! username || ! accessKey ) {
92- throw new Error ( 'BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY environment variables are required' ) ;
93- }
94-
95- // Start local HTTP server
96- await startHttpServer ( localPort , exampleDir ) ;
97-
98- // Start BrowserStack Local
99- await startBrowserStackLocal ( username , accessKey , localIdentifier ) ;
100- } ) ;
84+ const username = process . env . BROWSERSTACK_USERNAME ;
85+ const accessKey = process . env . BROWSERSTACK_ACCESS_KEY ;
86+ const localIdentifier = `local-${ Date . now ( ) } ` ;
87+ const localPort = 8080 ;
88+ const exampleDir = path . join ( repoRoot , 'packages' , 'plugin-button-click-counter' ) ;
89+
90+ test . beforeAll ( async ( ) => {
91+ if ( ! username || ! accessKey ) {
92+ throw new Error ( 'BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY environment variables are required' ) ;
93+ }
94+
95+ // Start local HTTP server
96+ await startHttpServer ( localPort , exampleDir ) ;
97+
98+ // Start BrowserStack Local
99+ await startBrowserStackLocal ( username , accessKey , localIdentifier ) ;
100+ } ) ;
101101
102102 test . afterAll ( async ( ) => {
103- // Stop BrowserStack Local
104- await stopBrowserStackLocal ( ) ;
105-
106- // Stop HTTP server
107- await stopHttpServer ( ) ;
108- } ) ;
109-
110- test ( 'should load example page and interact with button' , async ( ) => {
111- const clientPlaywrightVersion = execSync ( 'npx playwright --version' )
112- . toString ( )
113- . trim ( )
114- . split ( ' ' ) [ 1 ] ;
115-
116- const capabilities = {
117- browser : 'playwright-chromium' ,
118- os : 'osx' ,
119- os_version : 'sonoma' ,
120- name : 'Plugin Button Click Counter Example' ,
121- build : 'Training Repo Test' ,
122- project : 'BrowserStack Playwright Demo' ,
123- 'browserstack.username' : username ,
124- 'browserstack.accessKey' : accessKey ,
125- 'browserstack.local' : 'true' ,
126- 'browserstack.localIdentifier' : localIdentifier ,
127- 'client.playwrightVersion' : clientPlaywrightVersion ,
128- } ;
129-
130- const wsEndpoint = `wss://cdp.browserstack.com/playwright?caps=${ encodeURIComponent (
131- JSON . stringify ( capabilities )
132- ) } `;
133-
134- console . log ( 'BrowserStack capabilities:' , {
135- browser : capabilities . browser ,
136- os : capabilities . os ,
137- os_version : capabilities . os_version ,
138- name : capabilities . name ,
139- build : capabilities . build ,
140- project : capabilities . project ,
141- local : capabilities [ 'browserstack.local' ] ,
142- localIdentifier : capabilities [ 'browserstack.localIdentifier' ] ,
143- clientPlaywrightVersion : capabilities [ 'client.playwrightVersion' ] ,
144- hasUsername : Boolean ( capabilities [ 'browserstack.username' ] ) ,
145- hasAccessKey : Boolean ( capabilities [ 'browserstack.accessKey' ] ) ,
146- } ) ;
103+ // Stop BrowserStack Local
104+ await stopBrowserStackLocal ( ) ;
147105
148- const browser = await chromium . connect ( {
149- wsEndpoint ,
106+ // Stop HTTP server
107+ await stopHttpServer ( ) ;
150108 } ) ;
151109
152- const context = await browser . newContext ( ) ;
153- const page = await context . newPage ( ) ;
154-
155- try {
156- await page . goto ( `http://localhost:${ localPort } /examples/index.html` , {
157- waitUntil : 'networkidle' ,
158- timeout : 30000 ,
159- } ) ;
160-
161- const body = page . locator ( 'body' ) ;
162- await expect ( body ) . toBeVisible ( ) ;
163-
164- const buttons = page . locator ( 'button' ) ;
165- const buttonCount = await buttons . count ( ) ;
166-
167- if ( buttonCount > 0 ) {
168- const firstButton = buttons . first ( ) ;
169- await firstButton . click ( ) ;
170- console . log ( 'Clicked the first button' ) ;
171- }
172-
173- await expect ( body ) . toBeVisible ( ) ;
174- console . log ( 'Test passed: page loaded and interacted successfully' ) ;
175- } finally {
176- await context . close ( ) ;
177- await browser . close ( ) ;
178- }
179- } ) ;
110+ test ( 'should load example page and interact with button' , async ( ) => {
111+ const clientPlaywrightVersion = execSync ( 'npx playwright --version' )
112+ . toString ( )
113+ . trim ( )
114+ . split ( ' ' ) [ 1 ] ;
115+
116+ const capabilities = {
117+ browser : 'playwright-chromium' ,
118+ os : 'osx' ,
119+ os_version : 'sonoma' ,
120+ name : 'Plugin Button Click Counter Example' ,
121+ build : 'Training Repo Test' ,
122+ project : 'BrowserStack Playwright Demo' ,
123+ 'browserstack.username' : username ,
124+ 'browserstack.accessKey' : accessKey ,
125+ 'browserstack.local' : 'true' ,
126+ 'browserstack.localIdentifier' : localIdentifier ,
127+ 'client.playwrightVersion' : clientPlaywrightVersion ,
128+ } ;
129+
130+ const wsEndpoint = `wss://cdp.browserstack.com/playwright?caps=${ encodeURIComponent (
131+ JSON . stringify ( capabilities )
132+ ) } `;
133+
134+ console . log ( 'BrowserStack capabilities:' , {
135+ browser : capabilities . browser ,
136+ os : capabilities . os ,
137+ os_version : capabilities . os_version ,
138+ name : capabilities . name ,
139+ build : capabilities . build ,
140+ project : capabilities . project ,
141+ local : capabilities [ 'browserstack.local' ] ,
142+ localIdentifier : capabilities [ 'browserstack.localIdentifier' ] ,
143+ clientPlaywrightVersion : capabilities [ 'client.playwrightVersion' ] ,
144+ hasUsername : Boolean ( capabilities [ 'browserstack.username' ] ) ,
145+ hasAccessKey : Boolean ( capabilities [ 'browserstack.accessKey' ] ) ,
146+ } ) ;
147+
148+ const browser = await chromium . connect ( {
149+ wsEndpoint,
150+ } ) ;
151+
152+ const context = await browser . newContext ( ) ;
153+ const page = await context . newPage ( ) ;
154+
155+ try {
156+ await page . goto ( `http://localhost:${ localPort } /examples/index.html` , {
157+ waitUntil : 'networkidle' ,
158+ timeout : 30000 ,
159+ } ) ;
160+
161+ const body = page . locator ( 'body' ) ;
162+ await expect ( body ) . toBeVisible ( ) ;
163+
164+ // Wait for the click-counter button to appear
165+ const button = page . locator ( 'button' ) . first ( ) ;
166+ await expect ( button ) . toBeVisible ( { timeout : 10000 } ) ;
167+ await expect ( button ) . toBeEnabled ( ) ;
168+
169+ // Read the initial counter from the page text
170+ const initialBodyText = ( await body . textContent ( ) ) ?. trim ( ) ?? '' ;
171+ const initialMatch = initialBodyText . match ( / B u t t o n c l i c k s : \s * ( \d + ) / ) ;
172+
173+ if ( ! initialMatch ) {
174+ throw new Error ( `Could not find initial button click count in page text: "${ initialBodyText } "` ) ;
175+ }
176+
177+ let previousCount = Number ( initialMatch [ 1 ] ) ;
178+ console . log ( `Initial button click count: ${ previousCount } ` ) ;
179+
180+ // Click multiple times and verify the counter increments
181+ const clickAttempts = 3 ;
182+
183+ for ( let i = 1 ; i <= clickAttempts ; i += 1 ) {
184+ await button . click ( ) ;
185+
186+ await expect
187+ . poll ( async ( ) => {
188+ const currentBodyText = ( await body . textContent ( ) ) ?. trim ( ) ?? '' ;
189+ const currentMatch = currentBodyText . match ( / B u t t o n c l i c k s : \s * ( \d + ) / ) ;
190+ return currentMatch ? Number ( currentMatch [ 1 ] ) : null ;
191+ } , {
192+ timeout : 5000 ,
193+ message : `Expected counter to update after click ${ i } ` ,
194+ } )
195+ . toBe ( previousCount + 1 ) ;
196+
197+ previousCount += 1 ;
198+ console . log ( `Counter after click ${ i } : ${ previousCount } ` ) ;
199+
200+ await expect ( button ) . toBeVisible ( ) ;
201+ await expect ( button ) . toBeEnabled ( ) ;
202+ }
203+
204+ // Final safety check: page did not crash and button remains usable
205+ await expect ( body ) . toBeVisible ( ) ;
206+ await expect ( button ) . toBeVisible ( ) ;
207+ await expect ( button ) . toBeEnabled ( ) ;
208+
209+ console . log ( 'Test passed: click-counter button appeared, incremented after repeated clicks, and remained usable' ) ;
210+ } finally {
211+ await context . close ( ) ;
212+ await browser . close ( ) ;
213+ }
214+ } ) ;
180215} ) ;
0 commit comments