Skip to content

Commit 03651fa

Browse files
committed
AG-43023 improve tests run
Squashed commit of the following: commit 8227c24 Author: Slava Leleka <[email protected]> Date: Mon Jun 9 12:52:20 2025 -0400 fix changelog commit aaf8ab6 Author: Slava Leleka <[email protected]> Date: Mon Jun 9 12:42:38 2025 -0400 add MAX_CACHE_SIZE const commit 434f245 Author: Slava Leleka <[email protected]> Date: Thu Jun 5 00:41:07 2025 -0400 improve tests server commit bca62be Author: Slava Leleka <[email protected]> Date: Thu Jun 5 00:40:45 2025 -0400 improve runQunitTests commit 130f41f Author: Slava Leleka <[email protected]> Date: Thu Jun 5 00:04:54 2025 -0400 clean checkout for qunit tests commit 97b0364 Author: Slava Leleka <[email protected]> Date: Wed Jun 4 15:38:08 2025 -0400 test qunit first commit 5003a98 Author: Slava Leleka <[email protected]> Date: Wed Jun 4 15:36:19 2025 -0400 single job for lint commit be85368 Author: Slava Leleka <[email protected]> Date: Wed Jun 4 15:29:52 2025 -0400 single job for qunit tests commit 1776854 Author: Slava Leleka <[email protected]> Date: Wed Jun 4 15:25:42 2025 -0400 no clean checkout for qunit tests commit 6fa4ad0 Author: Slava Leleka <[email protected]> Date: Wed Jun 4 15:19:09 2025 -0400 split test and lint jobs to smaller ones commit cf195f6 Merge: c589ef6 c0c8be5 Author: Slava Leleka <[email protected]> Date: Wed Jun 4 15:12:39 2025 -0400 Merge branch 'master' into fix/AG-43023 commit c589ef6 Author: Slava Leleka <[email protected]> Date: Wed Jun 4 14:50:54 2025 -0400 clean up commit 186a233 Author: Slava Leleka <[email protected]> Date: Wed Jun 4 14:42:08 2025 -0400 fix typos commit 59bff8c Author: Slava Leleka <[email protected]> Date: Wed Jun 4 14:37:55 2025 -0400 split test specs into several jobs
1 parent c0c8be5 commit 03651fa

File tree

4 files changed

+209
-60
lines changed

4 files changed

+209
-60
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ The format is based on [Keep a Changelog], and this project adheres to [Semantic
1010
<!-- TODO: change `@added unknown` tag due to the actual version -->
1111
<!-- during new scriptlets or redirects releasing -->
1212

13-
## [v2.2.6] - 2025-06-04
13+
<!-- v2.2.6 is the same as v2.2.7 -->
14+
## [v2.2.7] - 2025-06-04
1415

1516
### Changed
1617

@@ -21,7 +22,7 @@ The format is based on [Keep a Changelog], and this project adheres to [Semantic
2122
- `json-prune` scriptlet to properly handle `null` values
2223
while checking specified key in object [#504].
2324

24-
[v2.2.6]: https://github.com/AdguardTeam/Scriptlets/compare/v2.2.4...v2.2.6
25+
[v2.2.7]: https://github.com/AdguardTeam/Scriptlets/compare/v2.2.4...v2.2.7
2526
[#504]: https://github.com/AdguardTeam/Scriptlets/issues/504
2627

2728
<!-- v2.2.5 is the same as v2.2.4 -->

bamboo-specs/test.yaml

Lines changed: 109 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,47 @@ variables:
88
dockerPuppeteer: adguard/puppeteer-runner:22.14--24.5--1
99

1010
stages:
11-
- Build:
11+
- Build: &stage
1212
manual: false
1313
final: false
1414
jobs:
15-
- Build
15+
- Build docs
16+
- Build lib
17+
- Lint and test:
18+
<<: *stage
19+
jobs:
20+
- Test qunit
21+
- Test vitest
22+
- Test smoke
23+
# lint should be run after building of docs and lib
24+
- Lint
25+
- Cleanup:
26+
<<: *stage
27+
final: true
28+
jobs:
29+
- Cleanup
1630

17-
Build:
18-
key: BUILD
19-
docker:
31+
Build docs:
32+
key: BUILDDOCS
33+
docker: &docker
2034
image: "${bamboo.dockerPuppeteer}"
2135
volumes:
2236
${system.PNPM_DIR}: "${bamboo.cachePnpm}"
2337
tasks:
2438
- checkout:
2539
force-clean-build: true
26-
- script:
40+
- script: &setup-script
2741
interpreter: SHELL
2842
scripts:
2943
- |-
3044
set -e
3145
set -x
3246
47+
ls -laht
48+
3349
# Fix mixed logs
3450
exec 2>&1
3551
36-
ls -laht
37-
3852
# Exclude '--ignore-scripts' from pnpm arguments
3953
# since it prevents postinstall scripts from running
4054
# so Chrome is not installed which is crucial for tests
@@ -43,17 +57,99 @@ Build:
4357
4458
# Install common dependencies
4559
pnpm install ${modifiedPnpmArgs}
46-
60+
- script:
61+
interpreter: SHELL
62+
scripts:
63+
- |-
4764
# build docs to lint them later
4865
# check compatibility table updates and build it and build wiki docs
4966
pnpm wiki
67+
requirements: &requirements
68+
- adg-docker: 'true'
69+
- extension: 'true'
5070

51-
pnpm test
71+
Build lib:
72+
key: BUILDLIB
73+
docker: *docker
74+
tasks:
75+
- checkout:
76+
force-clean-build: true
77+
- script: *setup-script
78+
- script:
79+
interpreter: SHELL
80+
scripts:
81+
- |-
5282
pnpm build
83+
artifacts:
84+
- name: scriptlets.corelibs.json
85+
location: dist
86+
pattern: scriptlets.corelibs.json
87+
required: true
88+
- name: redirects.json
89+
location: dist
90+
pattern: redirects.json
91+
required: true
92+
requirements: *requirements
93+
94+
Test qunit:
95+
key: TESTQUNIT
96+
docker: *docker
97+
tasks:
98+
- checkout:
99+
force-clean-build: true
100+
- script: *setup-script
101+
- script:
102+
interpreter: SHELL
103+
scripts:
104+
- |-
105+
pnpm test:qunit
106+
requirements: *requirements
107+
108+
Test vitest:
109+
key: TESTVITEST
110+
docker: *docker
111+
tasks:
112+
- checkout
113+
- script: *setup-script
114+
- script:
115+
interpreter: SHELL
116+
scripts:
117+
- |-
118+
pnpm test:vitest
119+
requirements: *requirements
120+
121+
Test smoke:
122+
key: TESTSMOKE
123+
docker: *docker
124+
tasks:
125+
- checkout:
126+
force-clean-build: true
127+
- script: *setup-script
128+
- script:
129+
interpreter: SHELL
130+
scripts:
131+
- |-
132+
pnpm test:smoke
133+
requirements: *requirements
53134

54-
# lint code and docs. it should be run after `pnpm build`
135+
Lint:
136+
key: LINT
137+
docker: *docker
138+
tasks:
139+
- checkout
140+
- script: *setup-script
141+
- script:
142+
interpreter: SHELL
143+
scripts:
144+
- |-
55145
pnpm lint
56-
final-tasks:
146+
requirements: *requirements
147+
148+
Cleanup:
149+
key: CLEAN
150+
docker: *docker
151+
tasks:
152+
- checkout
57153
- script:
58154
interpreter: SHELL
59155
scripts:
@@ -69,17 +165,7 @@ Build:
69165
echo "Size before cleanup:" && du -h | tail -n 1
70166
rm -rf node_modules
71167
echo "Size after cleanup:" && du -h | tail -n 1
72-
artifacts:
73-
- name: scriptlets.corelibs.json
74-
location: dist
75-
pattern: scriptlets.corelibs.json
76-
required: true
77-
- name: redirects.json
78-
location: dist
79-
pattern: redirects.json
80-
required: true
81-
requirements:
82-
- adg-docker: 'true'
168+
requirements: *requirements
83169

84170
branches:
85171
create: for-pull-request

tests/index.js

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
/* eslint-disable no-console */
22
import path from 'node:path';
33
import fs from 'node:fs';
4-
import { runQunitPuppeteer, printFailedTests, printResultSummary } from 'node-qunit-puppeteer';
4+
import { runQunitWithBrowser, printFailedTests, printResultSummary } from 'node-qunit-puppeteer';
55
import { fileURLToPath } from 'node:url';
6+
import puppeteer from 'puppeteer';
67

78
import {
89
server,
@@ -21,10 +22,12 @@ const TEST_FILE_NAME_MARKER = '.html';
2122
/**
2223
* Returns false if test failed and true if test passed
2324
*
24-
* @param {string} indexFile
25-
* @returns {Promise<boolean>}
25+
* @param {string} indexFile Path to the test file.
26+
* @param {puppeteer.Browser} browser Puppeteer browser instance to reuse.
27+
*
28+
* @returns {Promise<boolean>} Promise that resolves to true if test passed, false otherwise.
2629
*/
27-
const runQunit = async (indexFile) => {
30+
const runQunit = async (indexFile, browser) => {
2831
const qunitArgs = {
2932
targetUrl: `http://localhost:${port}/${indexFile}?test`,
3033
timeout: TESTS_RUN_TIMEOUT,
@@ -33,7 +36,7 @@ const runQunit = async (indexFile) => {
3336
puppeteerArgs: ['--no-sandbox', '--allow-file-access-from-files'],
3437
};
3538

36-
const result = await runQunitPuppeteer(qunitArgs);
39+
const result = await runQunitWithBrowser(browser, qunitArgs);
3740
printResultSummary(result, console);
3841
if (result.stats.failed > 0) {
3942
printFailedTests(result, console);
@@ -55,16 +58,44 @@ const runQunitTests = async () => {
5558
let testsPassed = true;
5659

5760
try {
58-
console.log('Running tests..');
61+
console.log('Running tests sequentially with shared browser instance..');
62+
63+
// Create a single browser instance to be shared across all tests
64+
const browser = await puppeteer.launch({
65+
args: ['--no-sandbox', '--allow-file-access-from-files'],
66+
// Using headless mode for better performance
67+
headless: 'new',
68+
});
5969

60-
// eslint-disable-next-line no-restricted-syntax
70+
// Run tests one after another
71+
const testResults = [];
6172
for (const fileName of testFiles) {
62-
// \n is needed to divide logging
63-
console.log(`\nTesting ${fileName}:`);
64-
// eslint-disable-next-line no-await-in-loop
65-
const testPassed = await runQunit(fileName);
66-
testsPassed = testsPassed && testPassed;
73+
console.log(`\nStarted test: ${fileName}`);
74+
try {
75+
const testPassed = await runQunit(fileName, browser);
76+
console.log(`Completed test: ${fileName}`);
77+
testResults.push({ fileName, passed: testPassed, error: null });
78+
} catch (error) {
79+
console.log(`Error in test ${fileName}:`, error);
80+
testResults.push({ fileName, passed: false, error });
81+
}
6782
}
83+
84+
// Close the shared browser instance
85+
await browser.close();
86+
87+
// Process results after all tests complete
88+
testResults.forEach(({ fileName, passed, error }) => {
89+
if (error) {
90+
console.log(`\n❌ Test ${fileName} failed with error:`, error);
91+
errorOccurred = true;
92+
} else if (!passed) {
93+
console.log(`\n❌ Test ${fileName} did not pass`);
94+
testsPassed = false;
95+
} else {
96+
console.log(`\n✅ Test ${fileName} passed`);
97+
}
98+
});
6899
} catch (e) {
69100
console.log(e);
70101
await stop(testServer);

tests/server.js

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ const TEST_QUERY_MARKER = '?test';
1111

1212
const PORT = 54136;
1313

14+
/**
15+
* Maximum file size for caching — 5MB.
16+
*/
17+
const MAX_CACHE_SIZE = 5 * 1024 * 1024;
18+
19+
/**
20+
* File cache to improve performance.
21+
*/
22+
const fileCache = new Map();
23+
1424
const mimeTypes = {
1525
'.html': 'text/html',
1626
'.js': 'application/javascript',
@@ -38,33 +48,54 @@ const server = {
3848
}
3949

4050
const fullPath = path.join(__dirname, 'dist', filename);
51+
const contentType = getContentType(fullPath);
4152

42-
fs.stat(fullPath, (err, stats) => {
43-
if (err) {
44-
console.log(err.message);
45-
res.writeHead(404);
46-
res.end(JSON.stringify(err));
47-
return;
48-
}
53+
// Check if file is cached and not a dynamic resource
54+
if (fileCache.has(fullPath) && !filename.includes('?')) {
55+
// Serve from cache
56+
console.log(`Serving ${filename} from cache`);
57+
res.writeHead(200, {
58+
'Content-Type': contentType,
59+
'Cache-Control': 'max-age=3600',
60+
});
61+
res.end(fileCache.get(fullPath));
62+
return;
63+
}
64+
65+
// Fast-path synchronous file check to avoid async overhead if we know the file exists
66+
if (!fs.existsSync(fullPath)) {
67+
res.writeHead(404);
68+
res.end('File not found');
69+
return;
70+
}
71+
72+
const stats = fs.statSync(fullPath);
73+
74+
if (stats.isFile()) {
75+
// It's a file, serve it
76+
try {
77+
const data = fs.readFileSync(fullPath);
78+
79+
// Cache the file if not too large
80+
if (stats.size < MAX_CACHE_SIZE) {
81+
fileCache.set(fullPath, data);
82+
}
4983

50-
if (stats.isFile()) {
51-
// It's a file, serve it
52-
fs.readFile(fullPath, (err, data) => {
53-
if (err) {
54-
console.log(err.message);
55-
res.writeHead(500);
56-
res.end(JSON.stringify(err));
57-
return;
58-
}
59-
res.writeHead(200, { 'Content-Type': getContentType(fullPath) });
60-
res.end(data);
84+
res.writeHead(200, {
85+
'Content-Type': contentType,
86+
'Cache-Control': 'max-age=3600',
6187
});
62-
} else {
63-
// Neither a file nor a directory
64-
res.writeHead(404);
65-
res.end();
88+
res.end(data);
89+
} catch (err) {
90+
console.log(err.message);
91+
res.writeHead(500);
92+
res.end(JSON.stringify(err));
6693
}
67-
});
94+
} else {
95+
// Neither a file nor a directory
96+
res.writeHead(404);
97+
res.end('Not a file');
98+
}
6899
});
69100
},
70101
};

0 commit comments

Comments
 (0)