Skip to content

Commit 277f019

Browse files
authored
Merge pull request #28 from developit/wip
SauceLabs, Custom Browsers & Modern JS
2 parents a391c9b + c802b6d commit 277f019

File tree

5 files changed

+118
-31
lines changed

5 files changed

+118
-31
lines changed

package.json

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"bin": "dist/cli.js",
88
"scripts": {
99
"prepare": "npm t",
10-
"build": "microbundle --target node --external all -f cjs --no-compress src/*.js",
10+
"build": "microbundle --target node -f cjs --no-compress src/*.js",
1111
"test:build": "node ./dist/cli.js run",
1212
"test:watch": "node ./dist/cli.js watch --headless false",
1313
"test": "eslint src test && npm run -s build && npm run -s test:build",
@@ -30,26 +30,28 @@
3030
"devDependencies": {
3131
"eslint": "^4.16.0",
3232
"eslint-config-developit": "^1.1.1",
33-
"microbundle": "^0.4.3",
33+
"microbundle": "^0.11.0",
3434
"webpack": "^4.14.0",
35-
"workerize-loader": "^1.0.1"
35+
"workerize-loader": "^1.0.4"
3636
},
3737
"dependencies": {
38-
"babel-core": "^6.26.0",
39-
"babel-loader": "^7.1.2",
38+
"@babel/core": "^7.4.3",
39+
"@babel/plugin-proposal-object-rest-spread": "^7.4.3",
40+
"@babel/plugin-transform-react-jsx": "^7.3.0",
41+
"@babel/polyfill": "^7.4.3",
42+
"@babel/preset-env": "^7.4.3",
43+
"@babel/preset-stage-0": "^7.0.0",
44+
"babel-loader": "^8.0.5",
4045
"babel-plugin-istanbul": "^5.1.0",
41-
"babel-plugin-transform-object-rest-spread": "^6.26.0",
42-
"babel-plugin-transform-react-jsx": "^6.24.1",
43-
"babel-polyfill": "^6.26.0",
44-
"babel-preset-env": "^1.6.1",
45-
"babel-preset-stage-0": "^6.24.1",
4646
"chalk": "^2.3.0",
4747
"dlv": "^1.1.1",
4848
"jasmine-core": "^3.3.0",
4949
"karma": "^3.1.1",
5050
"karma-chrome-launcher": "^2.2.0",
5151
"karma-coverage": "^1.1.2",
52+
"karma-firefox-launcher": "^1.1.0",
5253
"karma-jasmine": "^2.0.1",
54+
"karma-sauce-launcher": "^2.0.2",
5355
"karma-sourcemap-loader": "^0.3.7",
5456
"karma-spec-reporter": "0.0.32",
5557
"karma-webpack": "2.0.7",

src/cli.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@ import './lib/patch';
66
import karmatic from '.';
77
import { cleanStack } from './lib/util';
88

9+
// @ts-ignore
910
const { version } = require('../package.json');
1011

11-
let toArray = val => Array.isArray(val) ? val : val == null ? [] : [val];
12+
let toArray = val => typeof val === 'string' ? val.split(/\s*,\s*/) : val == null ? [] : [].concat(val);
1213

1314
let prog = sade('karmatic');
1415

1516
prog
1617
.version(version)
1718
.option('--files', 'Minimatch pattern for test files')
1819
.option('--headless', 'Run using Chrome Headless', true)
19-
.option('--coverage', 'Report code coverage of tests', true);
20+
.option('--coverage', 'Report code coverage of tests', true)
21+
.option('--downlevel', 'Downlevel syntax to ES5');
2022

2123
prog
2224
.command('run [...files]', '', { default: true })
@@ -32,6 +34,7 @@ prog
3234
.command('debug [...files]')
3335
.describe('Open a headful Puppeteer instance for debugging your tests')
3436
.option('--headless', 'Run using Chrome Headless', false) // Override default to false
37+
.option('--browsers', 'Run in specific browsers', null)
3538
.option('--coverage', 'Report code coverage of tests', false) // Override default to false
3639
.action( (str, opts) => run(str, opts, true) );
3740

@@ -40,6 +43,8 @@ prog.parse(process.argv);
4043
function run(str, opts, isWatch) {
4144
opts.watch = !!isWatch;
4245
opts.files = toArray(str || opts.files).concat(opts._);
46+
const b = opts.browsers || opts.browser;
47+
opts.browsers = b ? toArray(b) : null;
4348
karmatic(opts)
4449
.then( output => {
4550
if (output!=null) process.stdout.write(output + '\n');

src/configure.js

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
import path from 'path';
22
import puppeteer from 'puppeteer';
3+
import chalk from 'chalk';
34
import delve from 'dlv';
4-
import { moduleDir, tryRequire, dedupe, cleanStack, readFile, readDir } from './lib/util';
5+
import { tryRequire, dedupe, cleanStack, readFile, readDir } from './lib/util';
56
import babelLoader from './lib/babel-loader';
67
import cssLoader from './lib/css-loader';
78

89
const WEBPACK_VERSION = String(require('webpack').version || '3.0.0');
9-
const WEBPACK_MAJOR = WEBPACK_VERSION.split('.')[0]|0;
10+
const WEBPACK_MAJOR = parseInt(WEBPACK_VERSION.split('.')[0], 10);
1011

12+
/**
13+
* @param {Object} options
14+
* @param {Array} options.files - Test files to run
15+
* @param {Array} [options.browsers] - Custom list of browsers to run in
16+
* @param {Boolean} [options.headless=false] - Run in Headless Chrome?
17+
* @param {Boolean} [options.watch=false] - Start a continuous test server and retest when files change
18+
* @param {Boolean} [options.coverage=false] - Instrument and collect code coverage statistics
19+
* @param {Object} [options.webpackConfig] - Custom webpack configuration
20+
* @param {Boolean} [options.downlevel=false] - Downlevel/transpile syntax to ES5
21+
*/
1122
export default function configure(options) {
1223
let cwd = process.cwd(),
1324
res = file => path.resolve(cwd, file);
@@ -17,7 +28,9 @@ export default function configure(options) {
1728

1829
process.env.CHROME_BIN = puppeteer.executablePath();
1930

20-
let gitignore = (readFile(path.resolve(cwd, '.gitignore'), 'utf8') || '').replace(/(^\s*|\s*$|#.*$)/g, '').split('\n').filter(Boolean);
31+
let gitignore = (
32+
readFile(path.resolve(cwd, '.gitignore')) || ''
33+
).replace(/(^\s*|\s*$|#.*$)/g, '').split('\n').filter(Boolean);
2134
let repoRoot = (readDir(cwd) || []).filter( c => c[0]!=='.' && c!=='node_modules' && gitignore.indexOf(c)===-1 );
2235
let rootFiles = '{' + repoRoot.join(',') + '}';
2336

@@ -31,6 +44,56 @@ export default function configure(options) {
3144
options.coverage ? 'karma-coverage' : []
3245
);
3346

47+
// Custom launchers to be injected:
48+
const launchers = {};
49+
let useSauceLabs = false;
50+
51+
let browsers;
52+
if (options.browsers) {
53+
browsers = options.browsers.map(browser => {
54+
if (/^chrome$/i.test(browser)) {
55+
return 'Chrome';
56+
}
57+
if (/^firefox$/i.test(browser)) {
58+
PLUGINS.push('karma-firefox-launcher');
59+
return 'Firefox';
60+
}
61+
if (/^sauce-/.test(browser)) {
62+
if (!useSauceLabs) {
63+
useSauceLabs = true;
64+
PLUGINS.push('karma-sauce-launcher');
65+
}
66+
const parts = browser.toLowerCase().split('-');
67+
const name = parts.join('_');
68+
launchers[name] = {
69+
base: 'SauceLabs',
70+
browserName: parts[1].replace(/^ie$/gi, 'Internet Explorer'),
71+
version: parts[2] || undefined,
72+
platform: parts[3] ? parts[3].replace(/^win(dows)?[ -]+/gi, 'Windows ').replace(/^(macos|mac ?os ?x|os ?x)[ -]+/gi, 'OS X ') : undefined
73+
};
74+
return name;
75+
}
76+
return browser;
77+
});
78+
}
79+
else {
80+
browsers = [options.headless===false ? 'KarmaticChrome' : 'KarmaticChromeHeadless'];
81+
}
82+
83+
if (useSauceLabs) {
84+
let missing = ['SAUCE_USERNAME', 'SAUCE_ACCESS_KEY'].filter(x => !process.env[x])[0];
85+
if (missing) {
86+
throw (
87+
'\n' +
88+
chalk.bold.bgRed.white('Error:') + ' Missing SauceLabs auth configuration.' +
89+
'\n ' + chalk.white(`A SauceLabs browser was requested, but no ${chalk.magentaBright(missing)} environment variable provided.`) +
90+
'\n ' + chalk.white('Try prepending it to your test command:') +
91+
' ' + chalk.greenBright(missing + '=... npm test') +
92+
'\n'
93+
);
94+
}
95+
}
96+
3497
const WEBPACK_CONFIGS = [
3598
'webpack.config.babel.js',
3699
'webpack.config.js'
@@ -109,22 +172,27 @@ export default function configure(options) {
109172

110173
let generatedConfig = {
111174
basePath: cwd,
112-
plugins: PLUGINS.map(require.resolve),
175+
plugins: PLUGINS.map(req => require.resolve(req)),
113176
frameworks: ['jasmine'],
114177
reporters: ['spec'].concat(
115-
options.coverage ? 'coverage' : []
178+
options.coverage ? 'coverage' : [],
179+
useSauceLabs ? 'saucelabs' : []
116180
),
117-
browsers: [options.headless===false ? 'KarmaticChrome' : 'KarmaticChromeHeadless'],
181+
browsers,
182+
sauceLabs: {
183+
testName: pkg && pkg.name || undefined
184+
},
118185

119-
customLaunchers: {
186+
customLaunchers: Object.assign({
120187
KarmaticChrome: {
121-
base: 'Chrome'
188+
base: 'Chrome',
189+
flags: ['--no-sandbox']
122190
},
123191
KarmaticChromeHeadless: {
124192
base: 'ChromeHeadless',
125193
flags: ['--no-sandbox']
126194
}
127-
},
195+
}, launchers),
128196

129197
coverageReporter: {
130198
reporters: [
@@ -149,7 +217,8 @@ export default function configure(options) {
149217
}],
150218

151219
files: [
152-
{ pattern: moduleDir('babel-polyfill')+'/dist/polyfill.js', watched: false, included: true, served: true }
220+
// @TODO remove me
221+
// { pattern: moduleDir('babel-polyfill')+'/dist/polyfill.js', watched: false, included: true, served: true }
153222
].concat( ...files.map( pattern => {
154223
// Expand '**/xx' patterns but exempt node_modules and gitignored directories
155224
let matches = pattern.match(/^\*\*\/(.+)$/);
@@ -167,6 +236,7 @@ export default function configure(options) {
167236

168237
webpack: {
169238
devtool: 'cheap-module-eval-source-map',
239+
// devtool: 'module-source-map',
170240
mode: webpackConfig.mode || 'development',
171241
module: {
172242
// @TODO check webpack version and use loaders VS rules as the key here appropriately:

src/lib/babel-loader.js

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,33 @@ export default function babelLoader(options) {
22
return {
33
test: /\.jsx?$/,
44
exclude: /node_modules/,
5-
loader: 'babel-loader',
5+
loader: require.resolve('babel-loader'),
66
query: {
77
presets: [
8-
[require.resolve('babel-preset-env'), {
8+
[require.resolve('@babel/preset-env'), {
99
targets: {
10-
browsers: 'last 2 Chrome versions'
10+
browsers: [
11+
'last 2 Chrome versions',
12+
'last 2 Firefox versions',
13+
(options.downlevel || options.browsers && String(options.browsers).match(/(\bie(\b|\d)|internet.explorer)/gi)) && 'ie>=9'
14+
].filter(Boolean)
1115
},
16+
corejs: 2,
17+
useBuiltIns: 'usage',
1218
modules: false,
1319
loose: true
14-
}],
15-
require.resolve('babel-preset-stage-0')
20+
}]
1621
],
1722
plugins: [
18-
[require.resolve('babel-plugin-transform-object-rest-spread')],
19-
[require.resolve('babel-plugin-transform-react-jsx'), { pragma: options.pragma || 'h' }]
23+
[require.resolve('@babel/plugin-proposal-object-rest-spread'), {
24+
loose: true,
25+
useBuiltIns: true
26+
}],
27+
[require.resolve('@babel/plugin-transform-react-jsx'), {
28+
pragma: options.pragma || 'h'
29+
}]
2030
].concat(
21-
options.coverage ? require.resolve('babel-plugin-istanbul') : []
31+
options.coverage ? [require.resolve('babel-plugin-istanbul')] : []
2232
)
2333
}
2434
};

src/lib/css-loader.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export default function cssLoader() {
1+
export default function cssLoader(options) {
22
return {
33
test: /\.css$/,
44
loader: 'style-loader!css-loader'

0 commit comments

Comments
 (0)