Skip to content

Commit a2118df

Browse files
committed
Add new BrowserStack Playwright demo
1 parent 4271da6 commit a2118df

1 file changed

Lines changed: 176 additions & 141 deletions

File tree

browserstack-demo/browserstack-playwright.spec.js

Lines changed: 176 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -14,167 +14,202 @@ let bsLocal;
1414

1515
// Helper to start a local HTTP server
1616
function 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
3535
function 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
4949
function 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
7070
function 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

8383
test.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(/Button clicks:\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(/Button clicks:\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

Comments
 (0)