Skip to content

Commit 8dbfb7e

Browse files
committed
[FSSDK-12089] add setup for umd testing
1 parent 21fd8e1 commit 8dbfb7e

File tree

8 files changed

+560
-5
lines changed

8 files changed

+560
-5
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ local.log
1616

1717
**/*.gen.ts
1818

19-
.env
19+
.env

lib/index.umd.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { expect, describe, it } from 'vitest';
2+
3+
import * as optimizely from './index.browser';
4+
5+
type OptimizelySdk = typeof optimizely;
6+
7+
declare global {
8+
interface Window {
9+
optimizelySdk: OptimizelySdk;
10+
}
11+
}
12+
13+
describe('UMD Bundle', () => {
14+
// these are just intial tests to check the UMD bundle is loaded correctly
15+
// we will add more comprehensive umd tests later
16+
it('should have optimizelySdk on the window object', () => {
17+
expect(window.optimizelySdk).toBeDefined();
18+
});
19+
20+
it('should export createInstance function', () => {
21+
expect(typeof window.optimizelySdk.createInstance).toBe('function');
22+
});
23+
});

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
"test-browser": "node ./scripts/run-browser-tests.js",
6565
"test-browser-local": "USE_LOCAL_BROWSER=true node ./scripts/run-browser-tests.js",
6666
"test-browser-browserstack": "USE_LOCAL_BROWSER=false node ./scripts/run-browser-tests.js",
67+
"test-umd": "node ./scripts/run-umd-tests.js",
68+
"test-umd-local": "USE_LOCAL_BROWSER=true node ./scripts/run-umd-tests.js",
69+
"test-umd-browserstack": "USE_LOCAL_BROWSER=false node ./scripts/run-umd-tests.js",
6770
"test-mocha": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register -r tsconfig-paths/register -r lib/tests/exit_on_unhandled_rejection.js 'lib/**/*.tests.ts' 'lib/**/*.tests.js'",
6871
"test": "npm run test-mocha && npm run test-vitest",
6972
"posttest": "npm run lint",
@@ -74,7 +77,7 @@
7477
"prebuild": "npm run clean",
7578
"build": "npm run validate-platform-isolation && tsc --noEmit && npm run genmsg && rollup -c && cp dist/index.browser.d.ts dist/index.d.ts",
7679
"build:win": "tsc --noEmit && npm run genmsg && rollup -c && type nul > dist/optimizely.lite.es.d.ts && type nul > dist/optimizely.lite.es.min.d.ts && type nul > dist/optimizely.lite.min.d.ts",
77-
"build-browser-umd": "rollup -c --config-umd",
80+
"build-browser-umd": "npm run validate-platform-isolation && tsc --noEmit && npm run genmsg && rollup -c --config-umd",
7881
"coveralls": "nyc --reporter=lcov npm test",
7982
"prepare": "npm run build",
8083
"prepublishOnly": "npm test",

scripts/run-umd-tests.js

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Copyright 2025, Optimizely
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
const { execSync } = require('child_process');
19+
const browserstack = require('browserstack-local');
20+
const fs = require('fs');
21+
const path = require('path');
22+
23+
// Browser configurations grouped by browser name
24+
const BROWSER_CONFIGS = {
25+
chrome: [
26+
{ name: 'chrome-102-windows', browserVersion: '102', os: 'Windows', osVersion: '11' },
27+
{ name: 'chrome-latest-windows', browserVersion: 'latest', os: 'Windows', osVersion: '11' },
28+
],
29+
firefox: [
30+
{ name: 'firefox-91-windows', browserVersion: '91', os: 'Windows', osVersion: '11' },
31+
{ name: 'firefox-latest-windows', browserVersion: 'latest', os: 'Windows', osVersion: '11' },
32+
],
33+
edge: [
34+
{ name: 'edge-89-windows', browserVersion: '89', os: 'Windows', osVersion: '11' },
35+
{ name: 'edge-latest-windows', browserVersion: 'latest', os: 'Windows', osVersion: '11' },
36+
],
37+
safari: [
38+
{ name: 'safari-monterey', os: 'OS X', osVersion: 'Monterey' },
39+
{ name: 'safari-sequoia', os: 'OS X', osVersion: 'Sequoia' },
40+
]
41+
};
42+
43+
// Determine if we should use local browser or BrowserStack
44+
// Priority: USE_LOCAL_BROWSER env var, then check for BrowserStack credentials
45+
let useLocalBrowser = process.env.USE_LOCAL_BROWSER === 'true';
46+
47+
if (!useLocalBrowser) {
48+
// Check for BrowserStack credentials
49+
const username = process.env.BROWSERSTACK_USERNAME || process.env.BROWSER_STACK_USERNAME;
50+
const accessKey = process.env.BROWSERSTACK_ACCESS_KEY || process.env.BROWSER_STACK_ACCESS_KEY;
51+
52+
console.log('\n' + '='.repeat(80));
53+
console.log('BrowserStack Credentials Check:');
54+
console.log('='.repeat(80));
55+
console.log(`BROWSERSTACK_USERNAME: ${username ? '✓ Available' : '✗ Not found'}`);
56+
console.log(`BROWSERSTACK_ACCESS_KEY: ${accessKey ? '✓ Available' : '✗ Not found'}`);
57+
console.log('='.repeat(80) + '\n');
58+
59+
if (!username || !accessKey) {
60+
console.log('BrowserStack credentials not found - falling back to local browser mode');
61+
useLocalBrowser = true;
62+
}
63+
}
64+
65+
66+
let bs_local = null;
67+
68+
function startTunnel(localIdentifier) {
69+
const accessKey = process.env.BROWSERSTACK_ACCESS_KEY || process.env.BROWSER_STACK_ACCESS_KEY;
70+
71+
console.log(`Starting BrowserStack Local tunnel with identifier: ${localIdentifier}...`);
72+
bs_local = new browserstack.Local();
73+
const bsLocalArgs = {
74+
key: accessKey,
75+
force: true,
76+
forceLocal: true,
77+
// Enable verbose logging to debug tunnel issues
78+
verbose: true,
79+
// Use the provided identifier for parallel tunnel support
80+
localIdentifier: localIdentifier,
81+
};
82+
83+
return new Promise((resolve, reject) => {
84+
bs_local.start(bsLocalArgs, (error) => {
85+
if (error) {
86+
console.error('Error starting BrowserStack Local:', error);
87+
reject(error);
88+
} else {
89+
console.log('BrowserStack Local tunnel started successfully');
90+
console.log(`BrowserStack Local PID: ${bs_local.pid}`);
91+
console.log(`Local Identifier: ${localIdentifier}`);
92+
// Wait longer for tunnel to fully establish and register with BrowserStack
93+
console.log('Waiting for tunnel to establish...');
94+
setTimeout(() => {
95+
console.log('Tunnel ready!');
96+
resolve();
97+
}, 10000);
98+
}
99+
});
100+
});
101+
}
102+
103+
function stopTunnel() {
104+
if (!bs_local) {
105+
return Promise.resolve();
106+
}
107+
108+
return new Promise((resolve) => {
109+
bs_local.stop(() => {
110+
console.log('BrowserStack Local tunnel stopped');
111+
resolve();
112+
});
113+
});
114+
}
115+
116+
async function runTests() {
117+
let exitCode = 0;
118+
119+
try {
120+
// Step 1: Run npm run build
121+
console.log('\n' + '='.repeat(80));
122+
console.log('Building project...');
123+
console.log('='.repeat(80));
124+
try {
125+
execSync('npm run build-browser-umd', { stdio: 'inherit' });
126+
console.log('Build completed successfully!');
127+
} catch (error) {
128+
console.error('Failed to build project:', error.message);
129+
exitCode = 1;
130+
return;
131+
}
132+
133+
// Step 2: Copy the UMD file to vitest/public/dist/
134+
console.log('\n' + '='.repeat(80));
135+
console.log('Copying UMD file to vitest/public/dist/...');
136+
console.log('='.repeat(80));
137+
try {
138+
const sourceFile = path.join(process.cwd(), 'dist/optimizely.browser.umd.min.js');
139+
const destDir = path.join(process.cwd(), 'vitest/public/dist');
140+
const destFile = path.join(destDir, 'optimizely.browser.umd.min.js');
141+
142+
// Create destination directory if it doesn't exist
143+
if (!fs.existsSync(destDir)) {
144+
fs.mkdirSync(destDir, { recursive: true });
145+
console.log(`Created directory: ${destDir}`);
146+
}
147+
148+
// Copy the file
149+
fs.copyFileSync(sourceFile, destFile);
150+
console.log(`Copied ${sourceFile} to ${destFile}`);
151+
} catch (error) {
152+
console.error('Failed to copy UMD file:', error.message);
153+
exitCode = 1;
154+
return;
155+
}
156+
157+
// Patch Vitest viewport command to prevent WebDriver Bidi errors
158+
console.log('\n' + '='.repeat(80));
159+
console.log('Patching Vitest viewport command...');
160+
console.log('='.repeat(80));
161+
try {
162+
execSync('node ./scripts/patch-vitest-viewport.js', { stdio: 'inherit' });
163+
} catch (error) {
164+
console.error('Failed to patch Vitest viewport command:', error.message);
165+
exitCode = 1;
166+
return;
167+
}
168+
169+
// Get browser name from environment variable (default to chrome)
170+
const browserName = (process.env.TEST_BROWSER || 'chrome').toLowerCase();
171+
172+
let configs;
173+
174+
if (useLocalBrowser) {
175+
configs = [{
176+
name: `${browserName}`,
177+
}];
178+
console.log('Local browser mode: using local browser installation');
179+
} else {
180+
// For BrowserStack, use the defined configs
181+
configs = BROWSER_CONFIGS[browserName];
182+
if (!configs || configs.length === 0) {
183+
console.error(`Error: No configurations found for browser '${browserName}'`);
184+
console.error(`Available browsers: ${Object.keys(BROWSER_CONFIGS).join(', ')}`);
185+
exitCode = 1;
186+
return;
187+
}
188+
}
189+
190+
// Only start tunnel if using BrowserStack
191+
let localIdentifier;
192+
if (!useLocalBrowser) {
193+
// Generate a random identifier for parallel tunnel support (100000-900000)
194+
localIdentifier = Math.floor(Math.random() * 800000) + 100000;
195+
localIdentifier = localIdentifier.toString();
196+
await startTunnel(localIdentifier);
197+
} else {
198+
console.log('Using local browser mode - no BrowserStack connection needed');
199+
}
200+
201+
console.log('\n' + '='.repeat(80));
202+
console.log(`Running UMD tests for browser: ${browserName}`);
203+
console.log(`Total configurations: ${configs.length}`);
204+
console.log('='.repeat(80) + '\n');
205+
206+
const results = [];
207+
208+
// Run each config serially
209+
for (const config of configs) {
210+
console.log(`\n${'='.repeat(80)}`);
211+
console.log(`Running: ${config.name}`);
212+
console.log(`Browser: ${browserName}${config.browserVersion ? ` ${config.browserVersion}` : ''}`);
213+
console.log(`OS: ${config.os} ${config.osVersion}`);
214+
console.log('='.repeat(80));
215+
216+
// Set environment variables for this config
217+
const env = {
218+
...process.env,
219+
TEST_BROWSER: browserName,
220+
TEST_BROWSER_VERSION: config.browserVersion,
221+
TEST_OS_NAME: config.os,
222+
TEST_OS_VERSION: config.osVersion,
223+
// Pass the local identifier to vitest config for BrowserStack capabilities
224+
BROWSERSTACK_LOCAL_IDENTIFIER: localIdentifier,
225+
};
226+
227+
228+
try {
229+
console.log('Starting vitest UMD test...');
230+
// Run vitest with the UMD config
231+
execSync('npm run test-vitest -- --config vitest.umd.config.mts', {
232+
stdio: 'inherit',
233+
env,
234+
});
235+
236+
console.log(`\n✓ ${config.name} passed!`);
237+
results.push({ config: config.name, success: true });
238+
} catch (error) {
239+
console.error(`\n✗ ${config.name} failed`);
240+
if (error.message) {
241+
console.error('Error message:', error.message);
242+
}
243+
results.push({ config: config.name, success: false });
244+
}
245+
}
246+
247+
// Print summary
248+
console.log('\n' + '='.repeat(80));
249+
console.log(`UMD test summary for ${browserName}:`);
250+
console.log('='.repeat(80));
251+
252+
const failures = [];
253+
const successes = [];
254+
255+
results.forEach(({ config, success }) => {
256+
if (success) {
257+
successes.push(config);
258+
console.log(`✓ ${config}: PASSED`);
259+
} else {
260+
failures.push(config);
261+
console.error(`✗ ${config}: FAILED`);
262+
}
263+
});
264+
265+
console.log('='.repeat(80));
266+
console.log(`Total: ${results.length} configurations`);
267+
console.log(`Passed: ${successes.length}`);
268+
console.log(`Failed: ${failures.length}`);
269+
console.log('='.repeat(80));
270+
271+
// Set exit code based on results
272+
if (failures.length > 0) {
273+
console.error(`\nSome ${browserName} configurations failed. See above for details.`);
274+
exitCode = 1;
275+
} else {
276+
console.log(`\nAll ${browserName} configurations passed!`);
277+
exitCode = 0;
278+
}
279+
} finally {
280+
// Only stop tunnel if using BrowserStack
281+
if (!useLocalBrowser) {
282+
await stopTunnel();
283+
}
284+
285+
// Exit after tunnel is properly closed
286+
process.exit(exitCode);
287+
}
288+
}
289+
290+
// Run the tests
291+
runTests().catch((error) => {
292+
console.error('Fatal error:', error);
293+
process.exit(1);
294+
});

vitest.browser.config.mts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const testOsVersion = process.env.TEST_OS_VERSION;
4242
// Build local browser capabilities
4343
function buildLocalCapabilities() {
4444
return {
45-
testBrowser,
45+
browserName: testBrowser,
4646
'goog:chromeOptions': {
4747
args: [
4848
'--disable-blink-features=AutomationControlled',
@@ -149,8 +149,7 @@ export default defineConfig({
149149
test: {
150150
isolate: false,
151151
fileParallelism: true,
152-
// Reduce concurrency for BrowserStack to minimize tunnel load and WebSocket connection issues
153-
maxConcurrency: useLocalBrowser ? 5 : 1,
152+
maxConcurrency: 5,
154153
onConsoleLog: () => true,
155154
browser: {
156155
enabled: true,
@@ -167,6 +166,7 @@ export default defineConfig({
167166
exclude: [
168167
'lib/**/*.react_native.spec.ts',
169168
'lib/**/*.node.spec.ts',
169+
'lib/*.umd.spec.ts'
170170
],
171171
typecheck: {
172172
enabled: true,

vitest.config.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default defineConfig({
2727
onConsoleLog: () => true,
2828
environment: 'happy-dom',
2929
include: ['lib/**/*.spec.ts'],
30+
exclude: ['lib/*.umd.spec.ts'],
3031
typecheck: {
3132
enabled: true,
3233
tsconfig: 'tsconfig.spec.json',

0 commit comments

Comments
 (0)