testem 3.19.1 → 3.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +175 -134
- package/lib/api.js +1 -1
- package/lib/app.js +186 -116
- package/lib/cli-local-delegation.js +93 -0
- package/lib/config.js +260 -119
- package/lib/file_watcher.js +111 -37
- package/lib/file_watcher_impl.js +295 -0
- package/lib/launcher.js +7 -12
- package/lib/reporters/dev/runner_tabs.js +52 -47
- package/lib/reporters/dev/split_log_panel.js +1 -1
- package/lib/reporters/dev/toast_notify.js +5 -0
- package/lib/runners/browser_test_runner.js +3 -1
- package/lib/runners/hook_runner.js +0 -1
- package/lib/server/index.js +39 -10
- package/lib/tap_consumer.js +73 -11
- package/lib/utils/esbuild-browser-polyfills.js +1 -1
- package/lib/utils/esbuild-buffer-inject.js +1 -1
- package/lib/utils/expand_glob_pattern.js +19 -0
- package/lib/utils/file_watch_glob_policy.js +119 -0
- package/lib/utils/is_emfile_error.js +16 -0
- package/lib/utils/known-browsers.js +155 -118
- package/lib/utils/path_pattern_match.js +39 -0
- package/lib/utils/process.js +42 -0
- package/lib/utils/promises.js +20 -1
- package/lib/utils/reporter.js +2 -2
- package/lib/utils/tmp-cleanup.js +26 -0
- package/package.json +40 -31
- package/testem.js +174 -78
package/package.json
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testem",
|
|
3
|
-
"description": "Test'em 'scripts!
|
|
3
|
+
"description": "Test'em 'scripts! JavaScript unit testing made easy.",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"javascript",
|
|
6
6
|
"testing",
|
|
7
7
|
"unittest",
|
|
8
8
|
"browser"
|
|
9
9
|
],
|
|
10
|
-
"version": "3.
|
|
10
|
+
"version": "3.20.1",
|
|
11
11
|
"author": "Toby Ho <airportyh@gmail.com>",
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
14
|
-
"url": "
|
|
14
|
+
"url": "https://github.com/testem/testem.git"
|
|
15
15
|
},
|
|
16
16
|
"bugs": {
|
|
17
|
-
"url": "
|
|
17
|
+
"url": "https://github.com/testem/testem/issues"
|
|
18
18
|
},
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
20
|
+
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
|
21
21
|
},
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@xmldom/xmldom": "^0.
|
|
25
|
-
"backbone": "^1.1
|
|
26
|
-
"charm": "^1.0.
|
|
27
|
-
"
|
|
28
|
-
"
|
|
24
|
+
"@xmldom/xmldom": "^0.9.9",
|
|
25
|
+
"backbone": "^1.6.1",
|
|
26
|
+
"charm": "^1.0.2",
|
|
27
|
+
"chokidar": "^5.0.0",
|
|
28
|
+
"commander": "^14.0.3",
|
|
29
|
+
"compression": "^1.8.1",
|
|
29
30
|
"consolidate": "^1.0.4",
|
|
30
31
|
"execa": "^9.6.1",
|
|
31
|
-
"express": "^
|
|
32
|
-
"fireworm": "^0.7.2",
|
|
32
|
+
"express": "^5.2.1",
|
|
33
33
|
"glob": "^13.0.6",
|
|
34
|
-
"http-proxy": "^1.
|
|
35
|
-
"js-yaml": "^
|
|
36
|
-
"lodash": "^4.
|
|
34
|
+
"http-proxy": "^1.18.1",
|
|
35
|
+
"js-yaml": "^4.1.1",
|
|
36
|
+
"lodash": "^4.18.1",
|
|
37
|
+
"minimatch": "^10.2.5",
|
|
37
38
|
"mkdirp": "^3.0.1",
|
|
38
39
|
"mustache": "^4.2.0",
|
|
39
|
-
"
|
|
40
|
+
"toasted-notifier": "^10.1.0",
|
|
40
41
|
"printf": "^0.6.1",
|
|
41
|
-
"proc-log": "^
|
|
42
|
+
"proc-log": "^6.1.0",
|
|
42
43
|
"rimraf": "^6.1.3",
|
|
43
|
-
"socket.io": "^4.
|
|
44
|
+
"socket.io": "^4.8.3",
|
|
44
45
|
"spawn-args": "^0.2.0",
|
|
45
46
|
"styled_string": "0.0.1",
|
|
46
|
-
"tap-parser": "^
|
|
47
|
-
"tmp": "^0.2.5"
|
|
47
|
+
"tap-parser": "^18.3.0"
|
|
48
48
|
},
|
|
49
49
|
"files": [
|
|
50
50
|
"lib",
|
|
@@ -56,32 +56,41 @@
|
|
|
56
56
|
"views"
|
|
57
57
|
],
|
|
58
58
|
"scripts": {
|
|
59
|
-
"install:all": "npm install && npm install phantomjs-prebuilt",
|
|
60
59
|
"test": "mocha tests/*_tests.js tests/**/*_tests.js",
|
|
61
60
|
"test:verbose": "TESTEM_VERBOSE=1 mocha tests/*_tests.js tests/**/*_tests.js",
|
|
62
61
|
"cover": "cover run ./node_modules/.bin/_mocha tests/*_tests.js tests/**/*_tests.js; cover report html; open cover_html/index.html",
|
|
63
62
|
"integration": "node ./bin/run-integration.js",
|
|
63
|
+
"ci:safari-smoke": "node ./scripts/safari-ci-smoke.js",
|
|
64
64
|
"browser-tests": "cd examples/saucelabs/ && ../../testem.js ci -d",
|
|
65
65
|
"lint": "eslint ."
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@
|
|
69
|
-
"@
|
|
68
|
+
"@eslint/eslintrc": "^3.3.5",
|
|
69
|
+
"@eslint/js": "^10.0.1",
|
|
70
70
|
"chai": "^6.2.2",
|
|
71
|
-
"chai-files": "^1.
|
|
72
|
-
"chai-shallow-deep-equal": "^1.4.
|
|
73
|
-
"cheerio": "1.
|
|
71
|
+
"chai-files": "^1.4.0",
|
|
72
|
+
"chai-shallow-deep-equal": "^1.4.6",
|
|
73
|
+
"cheerio": "^1.2.0",
|
|
74
74
|
"dirty-chai": "^3.0.0",
|
|
75
|
-
"esbuild": "^0.
|
|
76
|
-
"
|
|
77
|
-
"eslint
|
|
75
|
+
"esbuild": "^0.27.4",
|
|
76
|
+
"esbuild-plugins-node-modules-polyfill": "^1.8.1",
|
|
77
|
+
"eslint": "^10.2.0",
|
|
78
|
+
"eslint-config-prettier": "^10.1.8",
|
|
79
|
+
"eslint-plugin-chai-expect": "^4.0.0",
|
|
78
80
|
"eslint-plugin-mocha": "^11.2.0",
|
|
79
|
-
"fresh-tape": "^5.
|
|
81
|
+
"fresh-tape": "^5.9.1",
|
|
80
82
|
"mocha": "^11.7.5",
|
|
81
83
|
"saucie": "^4.0.1",
|
|
82
84
|
"sinon": "^21.0.3",
|
|
83
85
|
"sinon-chai": "^4.0.1",
|
|
84
|
-
"socket.io-client": "^4.
|
|
86
|
+
"socket.io-client": "^4.8.3"
|
|
87
|
+
},
|
|
88
|
+
"overrides": {
|
|
89
|
+
"mocha": {
|
|
90
|
+
"diff": "^8.0.3",
|
|
91
|
+
"minimatch": "^9.0.7",
|
|
92
|
+
"serialize-javascript": "^7.0.5"
|
|
93
|
+
}
|
|
85
94
|
},
|
|
86
95
|
"bin": {
|
|
87
96
|
"testem": "./testem.js"
|
package/testem.js
CHANGED
|
@@ -1,76 +1,183 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
const { tryDelegateToLocalTestem } = require('./lib/cli-local-delegation');
|
|
4
|
+
if (require.main === module) {
|
|
5
|
+
tryDelegateToLocalTestem(__filename, process.argv, process.cwd(), process.env);
|
|
6
|
+
}
|
|
3
7
|
|
|
4
|
-
const
|
|
5
|
-
let progOptions = program;
|
|
8
|
+
const { Command } = require('commander');
|
|
6
9
|
const Config = require('./lib/config');
|
|
7
10
|
const Api = require('./lib/api');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
11
|
+
|
|
12
|
+
// this is to workaround the weird behavior in commander where
|
|
13
|
+
// if you provide additional command line arguments that aren't
|
|
14
|
+
// options, it goes in as a string as the 1st argument of the
|
|
15
|
+
// "action" callback, we don't want this
|
|
16
|
+
function act(fun) {
|
|
17
|
+
return function() {
|
|
18
|
+
let options = arguments[arguments.length - 1];
|
|
19
|
+
fun(options);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Build and parse a fresh Commander program from the given argv array.
|
|
25
|
+
* Returns { progOptions, appMode } where progOptions is a plain object
|
|
26
|
+
* containing all resolved option values (from program.opts() /
|
|
27
|
+
* optsWithGlobals()), so option values are directly accessible as
|
|
28
|
+
* properties rather than requiring access via a Command instance.
|
|
29
|
+
* A _commander reference is attached for internal option enumeration.
|
|
30
|
+
*
|
|
31
|
+
* @param {string[]} argv - process.argv-style array
|
|
32
|
+
* @param {{ exitOverride?: boolean }} [opts]
|
|
33
|
+
* exitOverride: when true, commander throws instead of calling process.exit
|
|
34
|
+
* (useful in tests to prevent the process from terminating)
|
|
35
|
+
*/
|
|
36
|
+
function parseArgs(argv, { exitOverride = false } = {}) {
|
|
37
|
+
let progOptions = null;
|
|
38
|
+
let appMode = 'dev';
|
|
39
|
+
|
|
40
|
+
const program = new Command();
|
|
41
|
+
|
|
42
|
+
if (exitOverride) {
|
|
43
|
+
program.exitOverride();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
program
|
|
47
|
+
.version(require(__dirname + '/package').version)
|
|
48
|
+
.usage('[options]')
|
|
49
|
+
.option(
|
|
50
|
+
'-f, --file [file]',
|
|
51
|
+
'config file path (JSON, YAML, or JS: testem.js / testem.cjs / testem.mjs); when omitted, picks the first match among defaults (see docs)',
|
|
52
|
+
)
|
|
53
|
+
.option('-p, --port [num]', 'server port - defaults to 7357', Number)
|
|
54
|
+
.option('--host [hostname]', 'host name - defaults to localhost', String)
|
|
55
|
+
.option(
|
|
56
|
+
'-l, --launch [list]',
|
|
57
|
+
'list of launchers to launch(comma separated)',
|
|
58
|
+
)
|
|
59
|
+
.option('-s, --skip [list]', 'list of launchers to skip(comma separated)')
|
|
60
|
+
.option(
|
|
61
|
+
'-d, --debug [file]',
|
|
62
|
+
'output debug to debug log - defaults to testem.log',
|
|
63
|
+
)
|
|
64
|
+
.option('-t, --test_page [page]', 'the html page to drive the tests')
|
|
65
|
+
.option('-g, --growl', 'turn on growl / native notifications')
|
|
66
|
+
// A root action is required in commander v7+ to prevent the program from
|
|
67
|
+
// automatically displaying help and exiting when no subcommand is given.
|
|
68
|
+
// Without it, running `testem` (dev mode) would show help instead of
|
|
69
|
+
// starting the dev server.
|
|
70
|
+
.action(() => {});
|
|
71
|
+
|
|
72
|
+
program
|
|
73
|
+
.command('launchers')
|
|
74
|
+
.description(
|
|
75
|
+
'Print the list of available launchers (browsers & process launchers)',
|
|
76
|
+
)
|
|
77
|
+
.action(
|
|
78
|
+
act((env) => {
|
|
79
|
+
progOptions = env.optsWithGlobals();
|
|
80
|
+
progOptions._commander = env;
|
|
81
|
+
appMode = 'launchers';
|
|
82
|
+
}),
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
program
|
|
86
|
+
.command('ci')
|
|
87
|
+
.description('Continuous integration mode')
|
|
88
|
+
.option(
|
|
89
|
+
'-T, --timeout [sec]',
|
|
90
|
+
'timeout a browser after [sec] seconds',
|
|
91
|
+
null,
|
|
92
|
+
)
|
|
93
|
+
.option(
|
|
94
|
+
'-P, --parallel [num]',
|
|
95
|
+
'number of browsers to run in parallel, defaults to 1',
|
|
96
|
+
Number,
|
|
97
|
+
)
|
|
98
|
+
.option('-b, --bail_on_uncaught_error', 'Bail on any uncaught errors')
|
|
99
|
+
.option(
|
|
100
|
+
'-R, --reporter [reporter]',
|
|
101
|
+
'Test reporter to use [tap|dot|xunit|teamcity]',
|
|
102
|
+
)
|
|
103
|
+
.option('--cwd [path]', 'directory to use as root')
|
|
104
|
+
.option(
|
|
105
|
+
'--config_dir [path]',
|
|
106
|
+
'directory to use as root for resolving configs, if different than cwd',
|
|
107
|
+
)
|
|
108
|
+
.action(
|
|
109
|
+
act((env) => {
|
|
110
|
+
progOptions = env.optsWithGlobals();
|
|
111
|
+
progOptions._commander = env;
|
|
112
|
+
appMode = 'ci';
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
program
|
|
117
|
+
.command('server')
|
|
118
|
+
.description('Run just the server')
|
|
119
|
+
.action(
|
|
120
|
+
act((env) => {
|
|
121
|
+
progOptions = env.optsWithGlobals();
|
|
122
|
+
progOptions._commander = env;
|
|
123
|
+
appMode = 'server';
|
|
124
|
+
}),
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
program.on('--help', () => {
|
|
128
|
+
console.log(' Keyboard Controls (in dev mode):\n');
|
|
129
|
+
console.log(' ENTER run the tests');
|
|
130
|
+
console.log(' q quit');
|
|
131
|
+
console.log(
|
|
132
|
+
' LEFT ARROW move to the next browser tab on the left',
|
|
133
|
+
);
|
|
134
|
+
console.log(
|
|
135
|
+
' RIGHT ARROW move to the next browser tab on the right',
|
|
136
|
+
);
|
|
137
|
+
console.log(
|
|
138
|
+
' TAB switch between top and bottom panel (split mode only)',
|
|
139
|
+
);
|
|
140
|
+
console.log(
|
|
141
|
+
' UP ARROW scroll up in the target text panel',
|
|
142
|
+
);
|
|
143
|
+
console.log(
|
|
144
|
+
' DOWN ARROW scroll down in the target text panel',
|
|
145
|
+
);
|
|
146
|
+
console.log(
|
|
147
|
+
' SPACE page down in the target text panel',
|
|
148
|
+
);
|
|
149
|
+
console.log(' b page up in the target text panel');
|
|
150
|
+
console.log(
|
|
151
|
+
' d half a page down in the target text panel',
|
|
152
|
+
);
|
|
153
|
+
console.log(
|
|
154
|
+
' u half a page up in the target text panel',
|
|
155
|
+
);
|
|
156
|
+
console.log();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
program.parse(argv);
|
|
160
|
+
|
|
161
|
+
if (progOptions === null) {
|
|
162
|
+
// dev mode: no subcommand was invoked; use opts() so option values are
|
|
163
|
+
// plain properties rather than requiring access via the Command object
|
|
164
|
+
// (commander v14 no longer exposes options as direct Command properties)
|
|
165
|
+
const devOpts = program.opts();
|
|
166
|
+
devOpts._commander = program;
|
|
167
|
+
progOptions = devOpts;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return { progOptions, appMode };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = { parseArgs };
|
|
174
|
+
|
|
175
|
+
if (require.main === module) {
|
|
176
|
+
main();
|
|
177
|
+
}
|
|
178
|
+
|
|
72
179
|
function main() {
|
|
73
|
-
|
|
180
|
+
const { progOptions, appMode } = parseArgs(process.argv);
|
|
74
181
|
|
|
75
182
|
let config = new Config(appMode, progOptions);
|
|
76
183
|
if (appMode === 'launchers') {
|
|
@@ -86,14 +193,3 @@ function main() {
|
|
|
86
193
|
}
|
|
87
194
|
}
|
|
88
195
|
}
|
|
89
|
-
|
|
90
|
-
// this is to workaround the weird behavior in command where
|
|
91
|
-
// if you provide additional command line arguments that aren't
|
|
92
|
-
// options, it goes in as a string as the 1st arguments of the
|
|
93
|
-
// "action" callback, we don't want this
|
|
94
|
-
function act(fun) {
|
|
95
|
-
return function() {
|
|
96
|
-
let options = arguments[arguments.length - 1];
|
|
97
|
-
fun(options);
|
|
98
|
-
};
|
|
99
|
-
}
|