8000 Tests: reuse browser workers in BrowserStack tests (#5428) · jquery/jquery@95a4c94 · GitHub
[go: up one dir, main page]

Skip to content

Commit 95a4c94

Browse files
authored
Tests: reuse browser workers in BrowserStack tests (#5428)
- reuse BrowserStack workers. - add support for "latest" and "latest-1" in browser version filters - add support for specifying non-final browser versions, such as beta versions - more accurate eslint for files in test/runner - switched `--no-isolate` command flag to `--isolate`. Now that browser instances are shared, it made more sense to me to default to no isolation unless specified. This turned out to be cleaner because the only place we isolate is in browserstack.yml. - fixed an issue with retries where it wasn't always waiting for the retried test run - enable strict mode in test yargs command
1 parent 2b97b6b commit 95a4c94

File tree

18 files changed

+539
-441
lines changed

18 files changed

+539
-441
lines changed

.github/workflows/browserstack.yml

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,25 @@ jobs:
1616
name: ${{ matrix.BROWSER }}
1717
concurrency:
1818
group: ${{ github.workflow }} ${{ matrix.BROWSER }}
19+
timeout-minutes: 30
1920
strategy:
2021
fail-fast: false
2122
matrix:
2223
BROWSER:
2324
- 'IE_11'
24-
- 'Safari_17'
25-
- 'Safari_16'
26-
- 'Chrome_120'
27-
- 'Chrome_119'
28-
- 'Edge_120'
29-
- 'Edge_119'
30-
- 'Firefox_121'
31-
- 'Firefox_120'
25+
- 'Safari_latest'
26+
- 'Safari_latest-1'
27+
- 'Chrome_latest'
28+
- 'Chrome_latest-1'
29+
- 'Opera_latest'
30+
- 'Edge_latest'
31+
- 'Edge_latest-1'
32+
- 'Firefox_latest'
33+
- 'Firefox_latest-1'
3234
- 'Firefox_115'
3335
- '__iOS_17'
3436
- '__iOS_16'
3537
- '__iOS_15'
36-
- 'Opera_106'
3738
steps:
3839
- name: Checkout
3940
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
@@ -61,4 +62,4 @@ jobs:
6162
run: npm run pretest
6263

6364
- name: Run tests
64-
run: npm run test:unit -- -v --browserstack "${{ matrix.BROWSER }}" --retries 3
65+
run: npm run test:unit -- -v --browserstack "${{ matrix.BROWSER }}" --run-id ${{ github.run_id }} --isolate --retries 3

eslint.config.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ export default [
2121
"test/node_smoke_tests/commonjs/**",
2222
"test/node_smoke_tests/module/**",
2323
"test/promises_aplus_adapters/**",
24-
"test/middleware-mockserver.cjs",
25-
"test/runner/**/*.js"
24+
"test/middleware-mockserver.cjs"
2625
],
2726
languageOptions: {
2827
globals: {
@@ -35,13 +34,6 @@ export default [
3534
}
3635
},
3736

38-
{
39-
files: [ "test/runner/listeners.js" ],
40-
languageOptions: {
41-
sourceType: "script"
42-
}
43-
},
44-
4537
// Source
4638
{
4739
files: [ "src/**" ],
@@ -222,6 +214,29 @@ export default [
222214
}
223215
},
224216

217+
{
218+
files: [
219+
"test/runner/**/*.js"
220+
],
221+
languageOptions: {
222+
globals: {
223+
...globals.node
224+
},
225+
sourceType: "module"
226+
},
227+
rules: {
228+
...jqueryConfig.rules
229+
}
230+
},
231+
232+
{
233+
files: [ "test/runner/listeners.js" ],
234+
languageOptions: {
235+
ecmaVersion: 5,
236+
sourceType: "script"
237+
}
238+
},
239+
225240
{
226241
files: [
227242
"test/data/testrunner.js",

package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,20 @@
5454
"pretest": "npm run qunit-fixture && npm run babel:tests && npm run npmcopy",
5555
"qunit-fixture": "node build/tasks/qunit-fixture.js",
5656
"start": "node -e \"require('./build/tasks/build.js').buildDefaultFiles({ watch: true })\"",
57-
"test:browser": "npm run pretest && npm run build:main && npm run test:unit -- -b chrome -b firefox --no-isolate -h",
57+
"test:browser": "npm run pretest && npm run build:main && npm run test:unit -- -b chrome -b firefox -h",
5858
"test:browserless": "npm run pretest && npm run build:all && node build/tasks/node_smoke_tests.js && node build/tasks/promises_aplus_tests.js && npm run test:unit -- -b jsdom -m basic",
5959
"test:jsdom": "npm run pretest && npm run build:main && npm run test:unit -- -b jsdom -m basic",
6060
"test:node_smoke_tests": "npm run pretest && npm run build:all && node build/tasks/node_smoke_tests.js",
6161
"test:promises_aplus": "npm run build:main && node build/tasks/promises_aplus_tests.js",
62-
"test:firefox": "npm run pretest && npm run build:main && npm run test:unit -- -v -b firefox --no-isolate -h",
63-
"test:safari": "npm run pretest && npm run build:main && npm run test:unit -- -b safari --no-isolate",
62+
"test:firefox": "npm run pretest && npm run build:main && npm run test:unit -- -v -b firefox -h",
63+
"test:safari": "npm run pretest && npm run build:main && npm run test:unit -- -b safari",
6464
"test:server": "node test/runner/server.js",
65-
"test:esm": "npm run pretest && npm run build:main && npm run test:unit -- --esm --no-isolate -h",
66-
"test:no-deprecated": "npm run pretest && npm run build -- -e deprecated && npm run test:unit -- --no-isolate -h",
67-
"test:selector-native": "npm run pretest && npm run build -- -e selector && npm run test:unit -- --no-isolate -h",
68-
"test:slim": "npm run pretest && npm run build -- --slim && npm run test:unit -- --no-isolate -h",
65+
"test:esm": "npm run pretest && npm run build:main && npm run test:unit -- --esm -h",
66+
"test:no-deprecated": "npm run pretest && npm run build -- -e deprecated && npm run test:unit -- -h",
67+
"test:selector-native": "npm run pretest && npm run build -- -e selector && npm run test:unit -- -h",
68+
"test:slim": "npm run pretest && npm run build -- --slim && npm run test:unit -- -h",
6969
"test:unit": "node test/runner/command.js",
70-
"test": "npm run build:all && npm run lint && npm run test:browserless && npm run test:browser && npm run test:esmodules && npm run test:slim && npm run test:no-deprecated && npm run test:selector-native"
70+
"test": "npm run build:all && npm run lint && npm run test:browserless && npm run test:browser && npm run test:esm && npm run test:slim && npm run test:no-deprecated && npm run test:selector-native"
7171
},
7272
"homepage": "https://jquery.com",
7373
"author": {

test/data/testinit.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,6 @@ this.loadTests = function() {
433433
}
434434

435435
} else {
436-
QUnit.load();
437436

438437
/**
439438
* Run in noConflict mode

test/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
// We need to read both.
3636
var esmodules = QUnit.config.esmodules || QUnit.urlParams.esmodules;
3737

38-
// `loadTests()` will call `QUnit.load()` because tests
38+
// `loadTests()` will call `QUnit.start()` because tests
3939
// such as unit/ready.js should run after document ready.
4040
if ( !esmodules ) {
4141
loadTests();

test/runner/browserstack/api.js

Lines changed: 76 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const accessKey = process.env.BROWSERSTACK_ACCESS_KEY;
1414
// iOS has null for version numbers,
1515
// and we do not need a similar check for OS versions.
1616
const rfinalVersion = /(?:^[0-9\.]+$)|(?:^null$)/;
17+
const rlatest = /^latest-(\d+)$/;
1718

1819
const rnonDigits = /(?:[^\d\.]+)|(?:20\d{2})/g;
1920

@@ -84,6 +85,15 @@ function compareVersionNumbers( a, b ) {
8485
return 1;
8586
}
8687
}
88+
89+
if ( rnonDigits.test( a ) && !rnonDigits.test( b ) ) {
90+
return -1;
91+
}
92+
if ( !rnonDigits.test( a ) && rnonDigits.test( b ) ) {
93+
return 1;
94+
}
95+
96+
return 0;
8797
}
8898

8999
function sortBrowsers( a, b ) {
@@ -148,17 +158,62 @@ export async function filterBrowsers( filter ) {
148158
const filterOsVersion = ( filter.os_version ?? "" ).toLowerCase();
149159
const filterDevice = ( filter.device ?? "" ).toLowerCase();
150160

151-
return browsers.filter( ( browser ) => {
161+
const filteredWithoutVersion = browsers.filter( ( browser ) => {
152162
return (
153163
( !filterBrowser || filterBrowser === browser.browser.toLowerCase() ) &&
154-
( !filterVersion ||
155-
matchVersion( browser.browser_version, filterVersion ) ) &&
156164
( !filterOs || filterOs === browser.os.toLowerCase() ) &&
157-
( !filterOsVersion ||
158-
filterOsVersion === browser.os_version.toLowerCase() ) &&
165+
( !filterOsVersion || matchVersion( browser.os_version, filterOsVersion ) ) &&
159166
( !filterDevice || filterDevice === ( browser.device || "" ).toLowerCase() )
160167
);
161168
} );
169+
170+
if ( !filterVersion ) {
171+
return filteredWithoutVersion;
172+
}
173+
174+
if ( filterVersion.startsWith( "latest" ) ) {
175+
const groupedByName = filteredWithoutVersion
176+
.filter( ( b ) => rfinalVersion.test( b.browser_version ) )
177+
.reduce( ( acc, browser ) => {
178+
acc[ browser.browser ] = acc[ browser.browser ] ?? [];
179+
acc[ browser.browser ].push( browser );
180+
return acc;
181+
}, Object.create( null ) );
182+
183+
const filtered = [];
184+
for ( const group of Object.values( groupedByName ) ) {
185+
const latest = group[ group.length - 1 ];
186+
187+
// Mobile devices do not have browser version.
188+
// Skip the version check for these,
189+
// but include the latest in the list if it made it
190+
// through filtering.
191+
if ( !latest.browser_version ) {
192+
193+
// Do not include in the list for latest-n.
194+
if ( filterVersion === "latest" ) {
195+
filtered.push( latest );
196+
}
197+
continue;
198+
}
199+
200+
// Get the latest version and subtract the number from the filter,
201+
// ignoring any patch versions, which may differ between major versions.
202+
const num = rlatest.exec( filterVersion );
203+
const version = parseInt( latest.browser_version ) - ( num ? num[ 1 ] : 0 );
204+
const match = group.findLast( ( browser ) => {
205+
return matchVersion( browser.browser_version, version.toString() );
206+
} );
207+
if ( match ) {
208+
filtered.push( match );
209+
}
210+
}
211+
return filtered;
212+
}
213+
214+
return filteredWithoutVersion.filter( ( browser ) => {
215+
return matchVersion( browser.browser_version, filterVersion );
216+
} );
162217
}
163218

164219
export async function listBrowsers( filter ) {
@@ -177,13 +232,11 @@ export async function listBrowsers( filter ) {
177232
}
178233

179234
export async function getLatestBrowser( filter ) {
235+
if ( !filter.browser_version ) {
236+
filter.browser_version = "latest";
237+
}
180238
const browsers = await filterBrowsers( filter );
181-
182-
// The list is sorted in ascending order,
183-
// so the last item is the latest.
184-
return browsers.findLast( ( browser ) =>
185-
rfinalVersion.test( browser.browser_version )
186-
);
239+
return browsers[ browsers.length - 1 ];
187240
}
188241

189242
/**
@@ -229,31 +282,14 @@ export function getWorker( id ) {
229282
return fetchAPI( `/worker/${ id }` );
230283
}
231284

232-
export async function deleteWorker( id, verbose ) {
233-
await fetchAPI( `/worker/${ id }`, { method: "DELETE" } );
234-
if ( verbose ) {
235-
console.log( `\nWorker ${ id } stopped.` );
236-
}
285+
export async function deleteWorker( id ) {
286+
return fetchAPI( `/worker/${ id }`, { method: "DELETE" } );
237287
}
238288

239289
export function getWorkers() {
240290
return fetchAPI( "/workers" );
241291
}
242292

243-
/**
244-
* Change the URL of a worker,
245-
* or refresh if it's the same URL.
246-
*/
247-
export function changeUrl( id, url ) {
248-
return fetchAPI( `/worker/${ id }/url.json`, {
249-
method: "PUT",
250-
body: JSON.stringify( {
251-
timeout: 20,
252-
url: encodeURI( url )
253-
} )
254-
} );
255-
}
256-
257293
/**
258294
* Stop all workers
259295
*/
@@ -262,15 +298,17 @@ export async function stopWorkers() {
262298

263299
// Run each request on its own
264300
// to avoid connect timeout errors.
301+
console.log( `${ workers.length } workers running...` );
265302
for ( const worker of workers ) {
266303
try {
267-
await deleteWorker( worker.id, true );
304+
await deleteWorker( worker.id );
268305
} catch ( error ) {
269306

270307
// Log the error, but continue trying to remove workers.
271308
console.error( error );
272309
}
273310
}
311+
console.log( "All workers stopped." );
274312
}
275313

276314
/**
@@ -284,6 +322,11 @@ export function getPlan() {
284322
}
285323

286324
export async function getAvailableSessions() {
287-
const [ plan, workers ] = await Promise.all( [ getPlan(), getWorkers() ] );
288-
return plan.parallel_sessions_max_allowed - workers.length;
325+
try {
326+
const [ plan, workers ] = await Promise.all( [ getPlan(), getWorkers() ] );
327+
return plan.parallel_sessions_max_allowed - workers.length;
328+
} catch ( error ) {
329+
console.error( error );
330+
return 0;
331+
}
289332
}

0 commit comments

Comments
 (0)
0