testem 3.19.1 → 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/app.js +186 -116
- package/lib/config.js +209 -109
- 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/split_log_panel.js +1 -1
- package/lib/runners/browser_test_runner.js +3 -1
- package/lib/runners/hook_runner.js +0 -1
- package/lib/server/index.js +31 -8
- 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 +37 -26
- 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
|
+
};
|
|
@@ -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,13 +111,14 @@ 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.promises
|
|
99
|
-
.
|
|
114
|
+
return fs.promises
|
|
115
|
+
.readFile(config.get('firefox_user_js'))
|
|
116
|
+
.then((content) => fs.promises.writeFile(perfJS, content))
|
|
100
117
|
.then(() => done(null), done);
|
|
101
118
|
}
|
|
102
119
|
|
|
103
|
-
//
|
|
104
|
-
//
|
|
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.
|
|
105
122
|
let prefs = [
|
|
106
123
|
'user_pref("browser.shell.checkDefaultBrowser", false);',
|
|
107
124
|
'user_pref("browser.cache.disk.smart_size.first_run", false);',
|
|
@@ -110,9 +127,34 @@ function firefoxSetup(browserTmpDir, config, done) {
|
|
|
110
127
|
'user_pref("app.update.auto", false);',
|
|
111
128
|
'user_pref("app.update.enabled", false);',
|
|
112
129
|
'user_pref("browser.EULA.override", true);',
|
|
113
|
-
'user_pref("toolkit.telemetry.reportingpolicy.firstRun", false);'
|
|
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.promises
|
|
156
|
+
fs.promises
|
|
157
|
+
.writeFile(perfJS, prefs.join(os.EOL))
|
|
116
158
|
.then(() => done(null), done);
|
|
117
159
|
}
|
|
118
160
|
|
|
@@ -138,7 +180,7 @@ module.exports = function knownBrowsers(platform, config) {
|
|
|
138
180
|
},
|
|
139
181
|
setup(config, done) {
|
|
140
182
|
firefoxSetup(this.browserTmpDir(), config, done);
|
|
141
|
-
}
|
|
183
|
+
},
|
|
142
184
|
},
|
|
143
185
|
{
|
|
144
186
|
name: 'Headless Firefox',
|
|
@@ -150,156 +192,156 @@ module.exports = function knownBrowsers(platform, config) {
|
|
|
150
192
|
},
|
|
151
193
|
setup(config, done) {
|
|
152
194
|
firefoxSetup(this.browserTmpDir(), config, done);
|
|
153
|
-
}
|
|
195
|
+
},
|
|
154
196
|
},
|
|
155
197
|
{
|
|
156
198
|
name: 'Brave',
|
|
157
|
-
possiblePath: braveWinPaths(userHomeDir, 'Brave-Browser').concat(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
],
|
|
199
|
+
possiblePath: braveWinPaths(userHomeDir, 'Brave-Browser').concat(
|
|
200
|
+
braveOSXPaths('Brave Browser'),
|
|
201
|
+
),
|
|
202
|
+
possibleExe: ['brave'],
|
|
161
203
|
args(config, url) {
|
|
162
204
|
return braveArgs(this.browserTmpDir(), url);
|
|
163
|
-
}
|
|
205
|
+
},
|
|
164
206
|
},
|
|
165
207
|
{
|
|
166
208
|
name: 'Headless Brave',
|
|
167
|
-
possiblePath: braveWinPaths(userHomeDir, 'Brave-Browser').concat(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
],
|
|
209
|
+
possiblePath: braveWinPaths(userHomeDir, 'Brave-Browser').concat(
|
|
210
|
+
braveOSXPaths('Brave Browser'),
|
|
211
|
+
),
|
|
212
|
+
possibleExe: ['brave'],
|
|
171
213
|
args(config, url) {
|
|
172
214
|
return headlessBraveArgs(this.browserTmpDir(), url);
|
|
173
|
-
}
|
|
215
|
+
},
|
|
174
216
|
},
|
|
175
217
|
{
|
|
176
218
|
name: 'Chrome',
|
|
177
|
-
possiblePath: chromeWinPaths(userHomeDir, 'Chrome').concat(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
'chrome'
|
|
182
|
-
],
|
|
219
|
+
possiblePath: chromeWinPaths(userHomeDir, 'Chrome').concat(
|
|
220
|
+
chromeOSXPaths('Google Chrome'),
|
|
221
|
+
),
|
|
222
|
+
possibleExe: ['google-chrome', 'google-chrome-stable', 'chrome'],
|
|
183
223
|
args(config, url) {
|
|
184
224
|
return chromeArgs(this.browserTmpDir(), url);
|
|
185
|
-
}
|
|
225
|
+
},
|
|
186
226
|
},
|
|
187
227
|
{
|
|
188
228
|
name: 'Headless Chrome',
|
|
189
|
-
possiblePath: chromeWinPaths(userHomeDir, 'Chrome').concat(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
'chrome'
|
|
194
|
-
],
|
|
229
|
+
possiblePath: chromeWinPaths(userHomeDir, 'Chrome').concat(
|
|
230
|
+
chromeOSXPaths('Google Chrome'),
|
|
231
|
+
),
|
|
232
|
+
possibleExe: ['google-chrome', 'google-chrome-stable', 'chrome'],
|
|
195
233
|
args(config, url) {
|
|
196
234
|
return headlessChromeArgs(this.browserTmpDir(), url);
|
|
197
|
-
}
|
|
235
|
+
},
|
|
198
236
|
},
|
|
199
237
|
|
|
200
238
|
// On Windows, Chrome Stable/Beta/Dev are installed on the same path
|
|
201
239
|
{
|
|
202
240
|
name: 'Chrome Beta',
|
|
203
241
|
possiblePath: chromeOSXPaths('Google Chrome Beta'),
|
|
204
|
-
possibleExe: [
|
|
205
|
-
'google-chrome-beta'
|
|
206
|
-
],
|
|
242
|
+
possibleExe: ['google-chrome-beta'],
|
|
207
243
|
args(config, url) {
|
|
208
244
|
return chromeArgs(this.browserTmpDir(), url);
|
|
209
|
-
}
|
|
245
|
+
},
|
|
210
246
|
},
|
|
211
247
|
{
|
|
212
248
|
name: 'Headless Chrome Beta',
|
|
213
249
|
possiblePath: chromeOSXPaths('Google Chrome Beta'),
|
|
214
|
-
possibleExe: [
|
|
215
|
-
'google-chrome-beta'
|
|
216
|
-
],
|
|
250
|
+
possibleExe: ['google-chrome-beta'],
|
|
217
251
|
args(config, url) {
|
|
218
252
|
return headlessChromeArgs(this.browserTmpDir(), url);
|
|
219
|
-
}
|
|
253
|
+
},
|
|
220
254
|
},
|
|
221
255
|
{
|
|
222
256
|
name: 'Chrome Dev',
|
|
223
257
|
possiblePath: chromeOSXPaths('Google Chrome Dev'),
|
|
224
|
-
possibleExe: [
|
|
225
|
-
'google-chrome-unstable'
|
|
226
|
-
],
|
|
258
|
+
possibleExe: ['google-chrome-unstable'],
|
|
227
259
|
args(config, url) {
|
|
228
260
|
return chromeArgs(this.browserTmpDir(), url);
|
|
229
|
-
}
|
|
261
|
+
},
|
|
230
262
|
},
|
|
231
263
|
|
|
232
264
|
// Chrome Canary is not available on Linux
|
|
233
265
|
{
|
|
234
266
|
name: 'Chrome Canary',
|
|
235
|
-
possiblePath: chromeWinPaths(userHomeDir, 'Chrome SxS').concat(
|
|
267
|
+
possiblePath: chromeWinPaths(userHomeDir, 'Chrome SxS').concat(
|
|
268
|
+
chromeOSXPaths('Google Chrome Canary'),
|
|
269
|
+
),
|
|
236
270
|
args(config, url) {
|
|
237
271
|
return chromeArgs(this.browserTmpDir(), url);
|
|
238
|
-
}
|
|
272
|
+
},
|
|
239
273
|
},
|
|
240
274
|
{
|
|
241
275
|
name: 'Chromium',
|
|
242
|
-
possiblePath: chromeWinPaths(userHomeDir, 'Chromium').concat(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
],
|
|
276
|
+
possiblePath: chromeWinPaths(userHomeDir, 'Chromium').concat(
|
|
277
|
+
chromeOSXPaths('Chromium'),
|
|
278
|
+
),
|
|
279
|
+
possibleExe: ['chromium-browser', 'chromium'],
|
|
247
280
|
args(config, url) {
|
|
248
281
|
return chromeArgs(this.browserTmpDir(), url);
|
|
249
|
-
}
|
|
282
|
+
},
|
|
250
283
|
},
|
|
251
284
|
{
|
|
252
285
|
name: 'Edge',
|
|
253
286
|
possiblePath: edgePaths(),
|
|
254
|
-
possibleExe: [
|
|
255
|
-
'microsoft-edge'
|
|
256
|
-
],
|
|
287
|
+
possibleExe: ['microsoft-edge'],
|
|
257
288
|
args(config, url) {
|
|
258
289
|
return chromeArgs(this.browserTmpDir(), url);
|
|
259
|
-
}
|
|
290
|
+
},
|
|
260
291
|
},
|
|
261
292
|
{
|
|
262
293
|
name: 'Headless Edge',
|
|
263
294
|
possiblePath: edgePaths(),
|
|
264
|
-
possibleExe: [
|
|
265
|
-
'microsoft-edge'
|
|
266
|
-
],
|
|
295
|
+
possibleExe: ['microsoft-edge'],
|
|
267
296
|
args(config, url) {
|
|
268
297
|
return headlessChromeArgs(this.browserTmpDir(), url);
|
|
269
|
-
}
|
|
270
|
-
},
|
|
271
|
-
{
|
|
272
|
-
name: 'Safari',
|
|
273
|
-
possiblePath: [
|
|
274
|
-
'C:\\Program Files\\Safari\\safari.exe',
|
|
275
|
-
'C:\\Program Files (x86)\\Safari\\safari.exe',
|
|
276
|
-
process.env.HOME + '/Applications/Safari.app/Contents/MacOS/Safari',
|
|
277
|
-
'/Applications/Safari.app/Contents/MacOS/Safari'
|
|
278
|
-
],
|
|
279
|
-
setup(config, done) {
|
|
280
|
-
let url = this.getUrl();
|
|
281
|
-
fs.writeFile(
|
|
282
|
-
path.join(this.browserTmpDir(), 'start.html'),
|
|
283
|
-
'<script>window.location = \'' + url + '\'</script>',
|
|
284
|
-
done
|
|
285
|
-
);
|
|
286
298
|
},
|
|
287
|
-
args() {
|
|
288
|
-
return [path.join(this.browserTmpDir(), 'start.html')];
|
|
289
|
-
}
|
|
290
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
|
+
},
|
|
291
333
|
{
|
|
292
334
|
name: 'Opera',
|
|
293
335
|
possiblePath: [
|
|
294
336
|
'C:\\Program Files\\Opera\\opera.exe',
|
|
295
337
|
'C:\\Program Files (x86)\\Opera\\opera.exe',
|
|
296
338
|
process.env.HOME + '/Applications/Opera.app/Contents/MacOS/Opera',
|
|
297
|
-
'/Applications/Opera.app/Contents/MacOS/Opera'
|
|
339
|
+
'/Applications/Opera.app/Contents/MacOS/Opera',
|
|
298
340
|
],
|
|
299
341
|
args(config, url) {
|
|
300
342
|
let operaDataDir = this.browserTmpDir();
|
|
301
343
|
return ['--user-data-dir=' + operaDataDir, '-pd', operaDataDir, url];
|
|
302
|
-
}
|
|
344
|
+
},
|
|
303
345
|
},
|
|
304
346
|
{
|
|
305
347
|
name: 'PhantomJS',
|
|
@@ -320,43 +362,38 @@ module.exports = function knownBrowsers(platform, config) {
|
|
|
320
362
|
options = phantom_args.concat(options);
|
|
321
363
|
}
|
|
322
364
|
return options;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
365
|
+
},
|
|
366
|
+
},
|
|
325
367
|
];
|
|
326
368
|
|
|
327
369
|
if (isWin(platform)) {
|
|
328
370
|
browsers.push({
|
|
329
371
|
name: 'IE',
|
|
330
|
-
possiblePath: 'C:\\Program Files\\Internet Explorer\\iexplore.exe'
|
|
372
|
+
possiblePath: 'C:\\Program Files\\Internet Explorer\\iexplore.exe',
|
|
331
373
|
});
|
|
332
|
-
} else {
|
|
374
|
+
} else if (platform === 'darwin') {
|
|
333
375
|
browsers.push({
|
|
376
|
+
// Same Launch Services approach as Safari on macOS (#1387).
|
|
334
377
|
name: 'Safari Technology Preview',
|
|
335
|
-
possiblePath:
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
setup(config, done) {
|
|
340
|
-
let url = this.getUrl();
|
|
341
|
-
fs.writeFile(
|
|
342
|
-
path.join(this.browserTmpDir(), 'start.html'),
|
|
343
|
-
'<script>window.location = \'' + url + '\'</script>',
|
|
344
|
-
done
|
|
345
|
-
);
|
|
378
|
+
possiblePath: '/usr/bin/open',
|
|
379
|
+
ignoreProcessExit: true,
|
|
380
|
+
args(config, url) {
|
|
381
|
+
return ['-a', 'Safari Technology Preview', url];
|
|
346
382
|
},
|
|
347
|
-
args() {
|
|
348
|
-
return [path.join(this.browserTmpDir(), 'start.html')];
|
|
349
|
-
}
|
|
350
383
|
});
|
|
351
384
|
}
|
|
352
385
|
|
|
353
386
|
// Add user-defined configs to browser objects
|
|
354
387
|
let browserPaths = config.get('browser_paths'); // Add config defined 'path' for binary
|
|
355
|
-
let browserExes = config.get('browser_exes');
|
|
388
|
+
let browserExes = config.get('browser_exes'); // Add config defined 'exe' for binary
|
|
356
389
|
if (browserPaths || browserExes) {
|
|
357
|
-
browsers.forEach(browserObject => {
|
|
358
|
-
let browserPath = browserPaths
|
|
359
|
-
|
|
390
|
+
browsers.forEach((browserObject) => {
|
|
391
|
+
let browserPath = browserPaths
|
|
392
|
+
? browserPaths[browserObject.name]
|
|
393
|
+
: undefined;
|
|
394
|
+
let browserExe = browserExes
|
|
395
|
+
? browserExes[browserObject.name]
|
|
396
|
+
: undefined;
|
|
360
397
|
if (browserPath) {
|
|
361
398
|
browserObject.possiblePath = browserPath;
|
|
362
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
|
+
};
|