testem 3.19.0 → 3.20.0
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 +32 -9
- package/lib/api.js +1 -1
- package/lib/app.js +204 -141
- package/lib/browser_launcher.js +5 -6
- package/lib/config.js +246 -133
- package/lib/file_watcher.js +111 -37
- package/lib/file_watcher_impl.js +295 -0
- package/lib/launcher.js +14 -23
- package/lib/log.js +92 -0
- package/lib/process-ctl.js +4 -4
- package/lib/reporters/dev/index.js +2 -1
- package/lib/reporters/dev/split_log_panel.js +2 -2
- package/lib/runners/browser_test_runner.js +15 -8
- package/lib/runners/hook_runner.js +13 -12
- package/lib/runners/process_test_runner.js +4 -5
- package/lib/runners/tap_process_test_runner.js +5 -6
- package/lib/runners/to-result.js +1 -1
- package/lib/server/index.js +62 -38
- package/lib/tap_consumer.js +74 -12
- package/lib/utils/browser-args.js +1 -1
- package/lib/utils/esbuild-browser-polyfills.js +46 -0
- package/lib/utils/esbuild-buffer-inject.js +6 -0
- package/lib/utils/expand_glob_pattern.js +19 -0
- package/lib/utils/file_watch_glob_policy.js +119 -0
- package/lib/utils/fileutils.js +4 -4
- package/lib/utils/is_emfile_error.js +16 -0
- package/lib/utils/known-browsers.js +161 -123
- package/lib/utils/path_pattern_match.js +39 -0
- package/lib/utils/process.js +45 -4
- package/lib/utils/promises.js +149 -0
- package/lib/utils/report-file.js +1 -3
- package/lib/utils/reporter.js +5 -5
- package/lib/utils/run-timeout.js +4 -4
- package/lib/utils/signal-listeners.js +9 -7
- package/lib/utils/tmp-cleanup.js +26 -0
- package/package.json +42 -32
- package/testem.js +170 -79
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const url = require('url');
|
|
2
|
+
|
|
3
|
+
const expandGlobPattern = require('./expand_glob_pattern');
|
|
4
|
+
const { convertToPosix } = require('./posix');
|
|
5
|
+
const { pathMatchesAny } = require('./path_pattern_match');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Normalize config `src_files`-style values to a list of glob strings.
|
|
9
|
+
* @param {string|string[]|Array<{src: string}>|undefined|null} value
|
|
10
|
+
* @returns {string[]}
|
|
11
|
+
*/
|
|
12
|
+
function flattenPatternList(value) {
|
|
13
|
+
if (value === undefined || value === null) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
return value.map(function(entry) {
|
|
18
|
+
if (typeof entry === 'string') {
|
|
19
|
+
return entry;
|
|
20
|
+
}
|
|
21
|
+
if (entry && typeof entry.src === 'string') {
|
|
22
|
+
return entry.src;
|
|
23
|
+
}
|
|
24
|
+
return String(entry);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return [String(value)];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Build the include / ignore pattern lists used by `FileWatcher` with the watch engine
|
|
32
|
+
* (policy filtering; the engine watches cwd and applies these patterns). Single place for glob +
|
|
33
|
+
* ignore semantics.
|
|
34
|
+
*
|
|
35
|
+
* @param {{ get: function(string): *, isCwdMode: function(): boolean }} config
|
|
36
|
+
* @returns {{ includePatterns: string[], ignorePatterns: string[] }}
|
|
37
|
+
*/
|
|
38
|
+
function buildWatchGlobPolicy(config) {
|
|
39
|
+
const includePatterns = [];
|
|
40
|
+
|
|
41
|
+
const confFile = config.get('file');
|
|
42
|
+
if (confFile) {
|
|
43
|
+
includePatterns.push(String(confFile));
|
|
44
|
+
}
|
|
45
|
+
if (config.isCwdMode()) {
|
|
46
|
+
includePatterns.push('*.js');
|
|
47
|
+
}
|
|
48
|
+
const watchFiles = config.get('watch_files');
|
|
49
|
+
if (watchFiles) {
|
|
50
|
+
includePatterns.push(...flattenPatternList(watchFiles));
|
|
51
|
+
}
|
|
52
|
+
const srcFiles = config.get('src_files');
|
|
53
|
+
const srcList = srcFiles !== undefined && srcFiles !== null ? srcFiles : '*.js';
|
|
54
|
+
includePatterns.push(...flattenPatternList(srcList));
|
|
55
|
+
|
|
56
|
+
const ignoreRaw = config.get('src_files_ignore');
|
|
57
|
+
const ignorePatterns = ignoreRaw ? flattenPatternList(ignoreRaw) : [];
|
|
58
|
+
|
|
59
|
+
return { includePatterns, ignorePatterns };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether a path would be treated as a watched file (matches an include pattern and
|
|
64
|
+
* no ignore pattern). Paths are normalized with {@link convertToPosix} for matching
|
|
65
|
+
* on Windows vs POSIX.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} filePath - Absolute or relative path as emitted by the filesystem / watcher.
|
|
68
|
+
* @param {{ includePatterns: string[], ignorePatterns: string[] }} policy
|
|
69
|
+
* @param {object} [matchOptions] - forwarded to minimatch (via path_pattern_match).
|
|
70
|
+
* @returns {boolean}
|
|
71
|
+
*/
|
|
72
|
+
function pathMatchesWatchTarget(filePath, policy, matchOptions) {
|
|
73
|
+
const posix = convertToPosix(filePath);
|
|
74
|
+
if (pathMatchesAny(posix, policy.ignorePatterns, matchOptions)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
return pathMatchesAny(posix, policy.includePatterns, matchOptions);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Expand all on-disk files matching the watch policy (same idea as `getFileSet` for
|
|
82
|
+
* `src_files`, but driven by the watch policy). Skips `http:` / `https:` URLs; keeps
|
|
83
|
+
* them as literal entries. Uses {@link expandGlobPattern} with `config.resolvePath`.
|
|
84
|
+
*
|
|
85
|
+
* @param {{ get: function(string): *, cwd: function(): string, resolvePath: function(string): string }} config
|
|
86
|
+
* @returns {Promise<string[]>} Sorted unique absolute paths (and any literal URLs).
|
|
87
|
+
*/
|
|
88
|
+
async function expandWatchableFilePaths(config) {
|
|
89
|
+
const policy = buildWatchGlobPolicy(config);
|
|
90
|
+
const ignorePosix = policy.ignorePatterns.map(function(p) {
|
|
91
|
+
return convertToPosix(config.resolvePath(p));
|
|
92
|
+
});
|
|
93
|
+
const out = [];
|
|
94
|
+
|
|
95
|
+
for (let i = 0; i < policy.includePatterns.length; i++) {
|
|
96
|
+
const pattern = policy.includePatterns[i];
|
|
97
|
+
const parsed = url.parse(pattern);
|
|
98
|
+
if (parsed.protocol && parsed.protocol !== 'file:' && parsed.protocol !== '') {
|
|
99
|
+
out.push(pattern);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
let fsPattern = pattern;
|
|
103
|
+
if (parsed.protocol === 'file:') {
|
|
104
|
+
fsPattern = parsed.hostname + parsed.path;
|
|
105
|
+
}
|
|
106
|
+
const resolved = convertToPosix(config.resolvePath(fsPattern));
|
|
107
|
+
const files = await expandGlobPattern(resolved, ignorePosix);
|
|
108
|
+
out.push(...files);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return [...new Set(out)].sort();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
buildWatchGlobPolicy,
|
|
116
|
+
flattenPatternList,
|
|
117
|
+
pathMatchesWatchTarget,
|
|
118
|
+
expandWatchableFilePaths,
|
|
119
|
+
};
|
package/lib/utils/fileutils.js
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const execa = require('execa').execa;
|
|
5
|
-
const
|
|
6
|
-
const log = require('
|
|
5
|
+
const { promisify } = require('util');
|
|
6
|
+
const log = require('../log');
|
|
7
7
|
|
|
8
|
-
const fsStatAsync =
|
|
8
|
+
const fsStatAsync = promisify(fs.stat);
|
|
9
9
|
|
|
10
10
|
const isWin = require('./is-win')();
|
|
11
11
|
|
|
12
12
|
exports.fileExists = function fileExists(path) {
|
|
13
|
-
return fsStatAsync(path).then(stat => stat.isFile()).
|
|
13
|
+
return fsStatAsync(path).then(stat => stat.isFile()).catch(() => false);
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
exports.executableExists = function executableExists(exe, options) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects EMFILE errors from `fs.watch` / watcher backends (e.g. chokidar `error`).
|
|
3
|
+
* Fireworm uses a dedicated `emfile` event; other engines surface this via `error`.
|
|
4
|
+
*
|
|
5
|
+
* @param {unknown} err
|
|
6
|
+
* @returns {boolean}
|
|
7
|
+
*/
|
|
8
|
+
function isEmfileError(err) {
|
|
9
|
+
return (
|
|
10
|
+
err !== null &&
|
|
11
|
+
typeof err === 'object' &&
|
|
12
|
+
/** @type {{ code?: string }} */ (err).code === 'EMFILE'
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = isEmfileError;
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
1
|
const browserArgs = require('./browser-args');
|
|
4
2
|
const path = require('path');
|
|
5
3
|
const fs = require('fs');
|
|
@@ -8,17 +6,20 @@ const os = require('os');
|
|
|
8
6
|
|
|
9
7
|
function chromeWinPaths(homeDir, name) {
|
|
10
8
|
return [
|
|
11
|
-
homeDir +
|
|
9
|
+
homeDir +
|
|
10
|
+
'\\Local Settings\\Application Data\\Google\\' +
|
|
11
|
+
name +
|
|
12
|
+
'\\Application\\chrome.exe',
|
|
12
13
|
homeDir + '\\AppData\\Local\\Google\\' + name + '\\Application\\chrome.exe',
|
|
13
14
|
'C:\\Program Files\\Google\\' + name + '\\Application\\Chrome.exe',
|
|
14
|
-
'C:\\Program Files (x86)\\Google\\' + name + '\\Application\\Chrome.exe'
|
|
15
|
+
'C:\\Program Files (x86)\\Google\\' + name + '\\Application\\Chrome.exe',
|
|
15
16
|
];
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
function chromeOSXPaths(name) {
|
|
19
20
|
return [
|
|
20
21
|
process.env.HOME + '/Applications/' + name + '.app/Contents/MacOS/' + name,
|
|
21
|
-
'/Applications/' + name + '.app/Contents/MacOS/' + name
|
|
22
|
+
'/Applications/' + name + '.app/Contents/MacOS/' + name,
|
|
22
23
|
];
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -31,7 +32,9 @@ function chromeArgs(browserTmpDir, url) {
|
|
|
31
32
|
'--test-type',
|
|
32
33
|
'--disable-renderer-backgrounding',
|
|
33
34
|
'--disable-background-timer-throttling',
|
|
34
|
-
|
|
35
|
+
'--disable-infobars',
|
|
36
|
+
'--disable-session-crashed-bubble',
|
|
37
|
+
url,
|
|
35
38
|
];
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -42,15 +45,24 @@ function headlessChromeArgs(browserTmpDir, url) {
|
|
|
42
45
|
'--disable-software-rasterizer',
|
|
43
46
|
'--mute-audio',
|
|
44
47
|
'--remote-debugging-port=0',
|
|
45
|
-
'--window-size=1440,900'
|
|
48
|
+
'--window-size=1440,900',
|
|
49
|
+
].concat(chromeArgs(browserTmpDir, url));
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
function braveWinPaths(homeDir, name) {
|
|
49
53
|
return [
|
|
50
|
-
homeDir +
|
|
51
|
-
|
|
54
|
+
homeDir +
|
|
55
|
+
'\\Local Settings\\Application Data\\BraveSoftware\\' +
|
|
56
|
+
name +
|
|
57
|
+
'\\Application\\brave.exe',
|
|
58
|
+
homeDir +
|
|
59
|
+
'\\AppData\\Local\\BraveSoftware\\' +
|
|
60
|
+
name +
|
|
61
|
+
'\\Application\\brave.exe',
|
|
52
62
|
'C:\\Program Files\\BraveSoftware\\' + name + '\\Application\\brave.exe',
|
|
53
|
-
'C:\\Program Files (x86)\\BraveSoftware\\' +
|
|
63
|
+
'C:\\Program Files (x86)\\BraveSoftware\\' +
|
|
64
|
+
name +
|
|
65
|
+
'\\Application\\brave.exe',
|
|
54
66
|
];
|
|
55
67
|
}
|
|
56
68
|
|
|
@@ -68,15 +80,17 @@ function headlessBraveArgs(browserTmpDir, url) {
|
|
|
68
80
|
'--disable-gpu',
|
|
69
81
|
'--mute-audio',
|
|
70
82
|
'--remote-debugging-port=0',
|
|
71
|
-
'--window-size=1440,900'
|
|
83
|
+
'--window-size=1440,900',
|
|
84
|
+
].concat(braveArgs(browserTmpDir, url));
|
|
72
85
|
}
|
|
73
86
|
|
|
74
87
|
function edgePaths() {
|
|
75
88
|
return [
|
|
76
89
|
'C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe',
|
|
77
90
|
'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',
|
|
78
|
-
process.env.HOME +
|
|
79
|
-
|
|
91
|
+
process.env.HOME +
|
|
92
|
+
'/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
|
|
93
|
+
'/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
|
|
80
94
|
];
|
|
81
95
|
}
|
|
82
96
|
|
|
@@ -84,9 +98,11 @@ function firefoxPaths() {
|
|
|
84
98
|
return [
|
|
85
99
|
'C:\\Program Files\\Mozilla Firefox\\firefox.exe',
|
|
86
100
|
'C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe',
|
|
87
|
-
'C:\\Users\\' +
|
|
101
|
+
'C:\\Users\\' +
|
|
102
|
+
process.env.USERNAME +
|
|
103
|
+
'\\AppData\\Local\\Mozilla Firefox\\firefox.exe',
|
|
88
104
|
process.env.HOME + '/Applications/Firefox.app/Contents/MacOS/firefox',
|
|
89
|
-
'/Applications/Firefox.app/Contents/MacOS/firefox'
|
|
105
|
+
'/Applications/Firefox.app/Contents/MacOS/firefox',
|
|
90
106
|
];
|
|
91
107
|
}
|
|
92
108
|
|
|
@@ -95,24 +111,51 @@ function firefoxSetup(browserTmpDir, config, done) {
|
|
|
95
111
|
let perfJS = path.join(browserTmpDir, 'user.js');
|
|
96
112
|
|
|
97
113
|
if (config.get('firefox_user_js')) {
|
|
98
|
-
return fs.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
fs.writeFile(perfJS, content, done);
|
|
104
|
-
});
|
|
114
|
+
return fs.promises
|
|
115
|
+
.readFile(config.get('firefox_user_js'))
|
|
116
|
+
.then((content) => fs.promises.writeFile(perfJS, content))
|
|
117
|
+
.then(() => done(null), done);
|
|
105
118
|
}
|
|
106
119
|
|
|
107
|
-
//
|
|
108
|
-
// and
|
|
120
|
+
// user.js: legacy Testem defaults plus prefs aligned with common Firefox --pref automation
|
|
121
|
+
// sets (welcome, onboarding, messaging, telemetry) and fewer prompts / background noise.
|
|
109
122
|
let prefs = [
|
|
110
123
|
'user_pref("browser.shell.checkDefaultBrowser", false);',
|
|
111
124
|
'user_pref("browser.cache.disk.smart_size.first_run", false);',
|
|
112
125
|
'user_pref("datareporting.policy.dataSubmissionEnabled", false);',
|
|
113
|
-
'user_pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "1481830156314");'
|
|
126
|
+
'user_pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "1481830156314");',
|
|
127
|
+
'user_pref("app.update.auto", false);',
|
|
128
|
+
'user_pref("app.update.enabled", false);',
|
|
129
|
+
'user_pref("browser.EULA.override", true);',
|
|
130
|
+
'user_pref("toolkit.telemetry.reportingpolicy.firstRun", false);',
|
|
131
|
+
'user_pref("browser.aboutwelcome.enabled", false);',
|
|
132
|
+
'user_pref("browser.startup.homepage", "about:blank");',
|
|
133
|
+
'user_pref("browser.startup.page", 0);',
|
|
134
|
+
'user_pref("browser.startup.firstrunSkipsHomepage", true);',
|
|
135
|
+
'user_pref("browser.startup.homepage_override.mstone", "ignore");',
|
|
136
|
+
'user_pref("browser.startup.homepage_welcome_url", "");',
|
|
137
|
+
'user_pref("browser.startup.homepage_welcome_url.additional", "");',
|
|
138
|
+
'user_pref("browser.startup.cohort", "ignore");',
|
|
139
|
+
'user_pref("browser.messaging-system.prompts.enabled", false);',
|
|
140
|
+
'user_pref("browser.onboarding.enabled", false);',
|
|
141
|
+
'user_pref("browser.tour.enabled", false);',
|
|
142
|
+
'user_pref("browser.startup.upgradeDialog.enabled", false);',
|
|
143
|
+
'user_pref("browser.uiCustomization.skipDefaultState", true);',
|
|
144
|
+
'user_pref("toolkit.telemetry.enabled", false);',
|
|
145
|
+
'user_pref("toolkit.telemetry.unified", false);',
|
|
146
|
+
'user_pref("browser.ping-centre.telemetry", false);',
|
|
147
|
+
'user_pref("browser.sessionstore.resume_from_crash", false);',
|
|
148
|
+
'user_pref("browser.tabs.warnOnClose", false);',
|
|
149
|
+
'user_pref("browser.tabs.warnOnCloseOtherTabs", false);',
|
|
150
|
+
'user_pref("browser.tabs.warnOnOpen", false);',
|
|
151
|
+
'user_pref("browser.download.manager.showWhenStarting", false);',
|
|
152
|
+
'user_pref("extensions.update.enabled", false);',
|
|
153
|
+
'user_pref("dom.webnotifications.enabled", false);',
|
|
154
|
+
'user_pref("gfx.direct2d.disabled", true);',
|
|
114
155
|
];
|
|
115
|
-
fs.
|
|
156
|
+
fs.promises
|
|
157
|
+
.writeFile(perfJS, prefs.join(os.EOL))
|
|
158
|
+
.then(() => done(null), done);
|
|
116
159
|
}
|
|
117
160
|
|
|
118
161
|
// Return the catalogue of the browsers that Testem supports for the platform. Each 'browser object'
|
|
@@ -137,7 +180,7 @@ module.exports = function knownBrowsers(platform, config) {
|
|
|
137
180
|
},
|
|
138
181
|
setup(config, done) {
|
|
139
182
|
firefoxSetup(this.browserTmpDir(), config, done);
|
|
140
|
-
}
|
|
183
|
+
},
|
|
141
184
|
},
|
|
142
185
|
{
|
|
143
186
|
name: 'Headless Firefox',
|
|
@@ -149,156 +192,156 @@ module.exports = function knownBrowsers(platform, config) {
|
|
|
149
192
|
},
|
|
150
193
|
setup(config, done) {
|
|
151
194
|
firefoxSetup(this.browserTmpDir(), config, done);
|
|
152
|
-
}
|
|
195
|
+
},
|
|
153
196
|
},
|
|
154
197
|
{
|
|
155
198
|
name: 'Brave',
|
|
156
|
-
possiblePath: braveWinPaths(userHomeDir, 'Brave-Browser').concat(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
],
|
|
199
|
+
possiblePath: braveWinPaths(userHomeDir, 'Brave-Browser').concat(
|
|
200
|
+
braveOSXPaths('Brave Browser'),
|
|
201
|
+
),
|
|
202
|
+
possibleExe: ['brave'],
|
|
160
203
|
args(config, url) {
|
|
161
204
|
return braveArgs(this.browserTmpDir(), url);
|
|
162
|
-
}
|
|
205
|
+
},
|
|
163
206
|
},
|
|
164
207
|
{
|
|
165
208
|
name: 'Headless Brave',
|
|
166
|
-
possiblePath: braveWinPaths(userHomeDir, 'Brave-Browser').concat(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
],
|
|
209
|
+
possiblePath: braveWinPaths(userHomeDir, 'Brave-Browser').concat(
|
|
210
|
+
braveOSXPaths('Brave Browser'),
|
|
211
|
+
),
|
|
212
|
+
possibleExe: ['brave'],
|
|
170
213
|
args(config, url) {
|
|
171
214
|
return headlessBraveArgs(this.browserTmpDir(), url);
|
|
172
|
-
}
|
|
215
|
+
},
|
|
173
216
|
},
|
|
174
217
|
{
|
|
175
218
|
name: 'Chrome',
|
|
176
|
-
possiblePath: chromeWinPaths(userHomeDir, 'Chrome').concat(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
'chrome'
|
|
181
|
-
],
|
|
219
|
+
possiblePath: chromeWinPaths(userHomeDir, 'Chrome').concat(
|
|
220
|
+
chromeOSXPaths('Google Chrome'),
|
|
221
|
+
),
|
|
222
|
+
possibleExe: ['google-chrome', 'google-chrome-stable', 'chrome'],
|
|
182
223
|
args(config, url) {
|
|
183
224
|
return chromeArgs(this.browserTmpDir(), url);
|
|
184
|
-
}
|
|
225
|
+
},
|
|
185
226
|
},
|
|
186
227
|
{
|
|
187
228
|
name: 'Headless Chrome',
|
|
188
|
-
possiblePath: chromeWinPaths(userHomeDir, 'Chrome').concat(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
'chrome'
|
|
193
|
-
],
|
|
229
|
+
possiblePath: chromeWinPaths(userHomeDir, 'Chrome').concat(
|
|
230
|
+
chromeOSXPaths('Google Chrome'),
|
|
231
|
+
),
|
|
232
|
+
possibleExe: ['google-chrome', 'google-chrome-stable', 'chrome'],
|
|
194
233
|
args(config, url) {
|
|
195
234
|
return headlessChromeArgs(this.browserTmpDir(), url);
|
|
196
|
-
}
|
|
235
|
+
},
|
|
197
236
|
},
|
|
198
237
|
|
|
199
238
|
// On Windows, Chrome Stable/Beta/Dev are installed on the same path
|
|
200
239
|
{
|
|
201
240
|
name: 'Chrome Beta',
|
|
202
241
|
possiblePath: chromeOSXPaths('Google Chrome Beta'),
|
|
203
|
-
possibleExe: [
|
|
204
|
-
'google-chrome-beta'
|
|
205
|
-
],
|
|
242
|
+
possibleExe: ['google-chrome-beta'],
|
|
206
243
|
args(config, url) {
|
|
207
244
|
return chromeArgs(this.browserTmpDir(), url);
|
|
208
|
-
}
|
|
245
|
+
},
|
|
209
246
|
},
|
|
210
247
|
{
|
|
211
248
|
name: 'Headless Chrome Beta',
|
|
212
249
|
possiblePath: chromeOSXPaths('Google Chrome Beta'),
|
|
213
|
-
possibleExe: [
|
|
214
|
-
'google-chrome-beta'
|
|
215
|
-
],
|
|
250
|
+
possibleExe: ['google-chrome-beta'],
|
|
216
251
|
args(config, url) {
|
|
217
252
|
return headlessChromeArgs(this.browserTmpDir(), url);
|
|
218
|
-
}
|
|
253
|
+
},
|
|
219
254
|
},
|
|
220
255
|
{
|
|
221
256
|
name: 'Chrome Dev',
|
|
222
257
|
possiblePath: chromeOSXPaths('Google Chrome Dev'),
|
|
223
|
-
possibleExe: [
|
|
224
|
-
'google-chrome-unstable'
|
|
225
|
-
],
|
|
258
|
+
possibleExe: ['google-chrome-unstable'],
|
|
226
259
|
args(config, url) {
|
|
227
260
|
return chromeArgs(this.browserTmpDir(), url);
|
|
228
|
-
}
|
|
261
|
+
},
|
|
229
262
|
},
|
|
230
263
|
|
|
231
264
|
// Chrome Canary is not available on Linux
|
|
232
265
|
{
|
|
233
266
|
name: 'Chrome Canary',
|
|
234
|
-
possiblePath: chromeWinPaths(userHomeDir, 'Chrome SxS').concat(
|
|
267
|
+
possiblePath: chromeWinPaths(userHomeDir, 'Chrome SxS').concat(
|
|
268
|
+
chromeOSXPaths('Google Chrome Canary'),
|
|
269
|
+
),
|
|
235
270
|
args(config, url) {
|
|
236
271
|
return chromeArgs(this.browserTmpDir(), url);
|
|
237
|
-
}
|
|
272
|
+
},
|
|
238
273
|
},
|
|
239
274
|
{
|
|
240
275
|
name: 'Chromium',
|
|
241
|
-
possiblePath: chromeWinPaths(userHomeDir, 'Chromium').concat(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
],
|
|
276
|
+
possiblePath: chromeWinPaths(userHomeDir, 'Chromium').concat(
|
|
277
|
+
chromeOSXPaths('Chromium'),
|
|
278
|
+
),
|
|
279
|
+
possibleExe: ['chromium-browser', 'chromium'],
|
|
246
280
|
args(config, url) {
|
|
247
281
|
return chromeArgs(this.browserTmpDir(), url);
|
|
248
|
-
}
|
|
282
|
+
},
|
|
249
283
|
},
|
|
250
284
|
{
|
|
251
285
|
name: 'Edge',
|
|
252
286
|
possiblePath: edgePaths(),
|
|
253
|
-
possibleExe: [
|
|
254
|
-
'microsoft-edge'
|
|
255
|
-
],
|
|
287
|
+
possibleExe: ['microsoft-edge'],
|
|
256
288
|
args(config, url) {
|
|
257
289
|
return chromeArgs(this.browserTmpDir(), url);
|
|
258
|
-
}
|
|
290
|
+
},
|
|
259
291
|
},
|
|
260
292
|
{
|
|
261
293
|
name: 'Headless Edge',
|
|
262
294
|
possiblePath: edgePaths(),
|
|
263
|
-
possibleExe: [
|
|
264
|
-
'microsoft-edge'
|
|
265
|
-
],
|
|
295
|
+
possibleExe: ['microsoft-edge'],
|
|
266
296
|
args(config, url) {
|
|
267
297
|
return headlessChromeArgs(this.browserTmpDir(), url);
|
|
268
|
-
}
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
name: 'Safari',
|
|
272
|
-
possiblePath: [
|
|
273
|
-
'C:\\Program Files\\Safari\\safari.exe',
|
|
274
|
-
'C:\\Program Files (x86)\\Safari\\safari.exe',
|
|
275
|
-
process.env.HOME + '/Applications/Safari.app/Contents/MacOS/Safari',
|
|
276
|
-
'/Applications/Safari.app/Contents/MacOS/Safari'
|
|
277
|
-
],
|
|
278
|
-
setup(config, done) {
|
|
279
|
-
let url = this.getUrl();
|
|
280
|
-
fs.writeFile(
|
|
281
|
-
path.join(this.browserTmpDir(), 'start.html'),
|
|
282
|
-
'<script>window.location = \'' + url + '\'</script>',
|
|
283
|
-
done
|
|
284
|
-
);
|
|
285
298
|
},
|
|
286
|
-
args() {
|
|
287
|
-
return [path.join(this.browserTmpDir(), 'start.html')];
|
|
288
|
-
}
|
|
289
299
|
},
|
|
300
|
+
platform === 'darwin'
|
|
301
|
+
? {
|
|
302
|
+
// Use Launch Services so Safari opens the http(s) URL directly. Passing a local
|
|
303
|
+
// start.html file makes Safari 13+ prompt for confirmation (#1387) and breaks CI.
|
|
304
|
+
name: 'Safari',
|
|
305
|
+
possiblePath: '/usr/bin/open',
|
|
306
|
+
// `open` exits right after handing off to Safari; Testem must not treat that as the
|
|
307
|
+
// browser process dying (browser_test_runner ignoreProcessExit).
|
|
308
|
+
ignoreProcessExit: true,
|
|
309
|
+
args(config, url) {
|
|
310
|
+
return ['-a', 'Safari', url];
|
|
311
|
+
},
|
|
312
|
+
}
|
|
313
|
+
: {
|
|
314
|
+
name: 'Safari',
|
|
315
|
+
possiblePath: [
|
|
316
|
+
'C:\\Program Files\\Safari\\safari.exe',
|
|
317
|
+
'C:\\Program Files (x86)\\Safari\\safari.exe',
|
|
318
|
+
process.env.HOME + '/Applications/Safari.app/Contents/MacOS/Safari',
|
|
319
|
+
'/Applications/Safari.app/Contents/MacOS/Safari',
|
|
320
|
+
],
|
|
321
|
+
setup(config, done) {
|
|
322
|
+
let url = this.getUrl();
|
|
323
|
+
fs.writeFile(
|
|
324
|
+
path.join(this.browserTmpDir(), 'start.html'),
|
|
325
|
+
"<script>window.location = '" + url + "'</script>",
|
|
326
|
+
done,
|
|
327
|
+
);
|
|
328
|
+
},
|
|
329
|
+
args() {
|
|
330
|
+
return [path.join(this.browserTmpDir(), 'start.html')];
|
|
331
|
+
},
|
|
332
|
+
},
|
|
290
333
|
{
|
|
291
334
|
name: 'Opera',
|
|
292
335
|
possiblePath: [
|
|
293
336
|
'C:\\Program Files\\Opera\\opera.exe',
|
|
294
337
|
'C:\\Program Files (x86)\\Opera\\opera.exe',
|
|
295
338
|
process.env.HOME + '/Applications/Opera.app/Contents/MacOS/Opera',
|
|
296
|
-
'/Applications/Opera.app/Contents/MacOS/Opera'
|
|
339
|
+
'/Applications/Opera.app/Contents/MacOS/Opera',
|
|
297
340
|
],
|
|
298
341
|
args(config, url) {
|
|
299
342
|
let operaDataDir = this.browserTmpDir();
|
|
300
343
|
return ['--user-data-dir=' + operaDataDir, '-pd', operaDataDir, url];
|
|
301
|
-
}
|
|
344
|
+
},
|
|
302
345
|
},
|
|
303
346
|
{
|
|
304
347
|
name: 'PhantomJS',
|
|
@@ -319,43 +362,38 @@ module.exports = function knownBrowsers(platform, config) {
|
|
|
319
362
|
options = phantom_args.concat(options);
|
|
320
363
|
}
|
|
321
364
|
return options;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
365
|
+
},
|
|
366
|
+
},
|
|
324
367
|
];
|
|
325
368
|
|
|
326
369
|
if (isWin(platform)) {
|
|
327
370
|
browsers.push({
|
|
328
371
|
name: 'IE',
|
|
329
|
-
possiblePath: 'C:\\Program Files\\Internet Explorer\\iexplore.exe'
|
|
372
|
+
possiblePath: 'C:\\Program Files\\Internet Explorer\\iexplore.exe',
|
|
330
373
|
});
|
|
331
|
-
} else {
|
|
374
|
+
} else if (platform === 'darwin') {
|
|
332
375
|
browsers.push({
|
|
376
|
+
// Same Launch Services approach as Safari on macOS (#1387).
|
|
333
377
|
name: 'Safari Technology Preview',
|
|
334
|
-
possiblePath:
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
setup(config, done) {
|
|
339
|
-
let url = this.getUrl();
|
|
340
|
-
fs.writeFile(
|
|
341
|
-
path.join(this.browserTmpDir(), 'start.html'),
|
|
342
|
-
'<script>window.location = \'' + url + '\'</script>',
|
|
343
|
-
done
|
|
344
|
-
);
|
|
378
|
+
possiblePath: '/usr/bin/open',
|
|
379
|
+
ignoreProcessExit: true,
|
|
380
|
+
args(config, url) {
|
|
381
|
+
return ['-a', 'Safari Technology Preview', url];
|
|
345
382
|
},
|
|
346
|
-
args() {
|
|
347
|
-
return [path.join(this.browserTmpDir(), 'start.html')];
|
|
348
|
-
}
|
|
349
383
|
});
|
|
350
384
|
}
|
|
351
385
|
|
|
352
386
|
// Add user-defined configs to browser objects
|
|
353
387
|
let browserPaths = config.get('browser_paths'); // Add config defined 'path' for binary
|
|
354
|
-
let browserExes = config.get('browser_exes');
|
|
388
|
+
let browserExes = config.get('browser_exes'); // Add config defined 'exe' for binary
|
|
355
389
|
if (browserPaths || browserExes) {
|
|
356
|
-
browsers.forEach(browserObject => {
|
|
357
|
-
let browserPath = browserPaths
|
|
358
|
-
|
|
390
|
+
browsers.forEach((browserObject) => {
|
|
391
|
+
let browserPath = browserPaths
|
|
392
|
+
? browserPaths[browserObject.name]
|
|
393
|
+
: undefined;
|
|
394
|
+
let browserExe = browserExes
|
|
395
|
+
? browserExes[browserObject.name]
|
|
396
|
+
: undefined;
|
|
359
397
|
if (browserPath) {
|
|
360
398
|
browserObject.possiblePath = browserPath;
|
|
361
399
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const { minimatch } = require('minimatch');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Match a path against a single glob pattern. Thin wrapper so the matcher
|
|
5
|
+
* implementation (minimatch today, possibly picomatch later) lives in one place.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} filePath - Path to test (forward slashes recommended; callers often use `convertToPosix`).
|
|
8
|
+
* @param {string} pattern - A single glob pattern.
|
|
9
|
+
* @param {import('minimatch').MinimatchOptions} [options] - Passed through to the matcher.
|
|
10
|
+
* @returns {boolean}
|
|
11
|
+
*/
|
|
12
|
+
function pathMatchesPattern(filePath, pattern, options) {
|
|
13
|
+
if (pattern === null || pattern === undefined || pattern === '') {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return minimatch(filePath, pattern, options);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* True if `filePath` matches **any** non-empty pattern in the list.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} filePath
|
|
23
|
+
* @param {string[]} patterns
|
|
24
|
+
* @param {import('minimatch').MinimatchOptions} [options]
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
27
|
+
function pathMatchesAny(filePath, patterns, options) {
|
|
28
|
+
if (!patterns || patterns.length === 0) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return patterns.some(function(p) {
|
|
32
|
+
return p && pathMatchesPattern(filePath, p, options);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
pathMatchesPattern,
|
|
38
|
+
pathMatchesAny,
|
|
39
|
+
};
|