From a656ed7f3e8480be32519645097ba11a3e4f761f Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Tue, 9 Apr 2019 17:05:18 -0400 Subject: [PATCH] wip --- package.json | 22 ++++++----- src/cli.js | 6 ++- src/configure.js | 88 ++++++++++++++++++++++++++++++++++++----- src/lib/babel-loader.js | 26 ++++++++---- src/lib/css-loader.js | 2 +- 5 files changed, 114 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 8658716..6b61ac1 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "bin": "dist/cli.js", "scripts": { "prepare": "npm t", - "build": "microbundle --target node --external all -f cjs --no-compress src/*.js", + "build": "microbundle --target node -f cjs --no-compress src/*.js", "test:build": "node ./dist/cli.js run", "test:watch": "node ./dist/cli.js watch --headless false", "test": "eslint src test && npm run -s build && npm run -s test:build", @@ -30,26 +30,28 @@ "devDependencies": { "eslint": "^4.16.0", "eslint-config-developit": "^1.1.1", - "microbundle": "^0.4.3", + "microbundle": "^0.11.0", "webpack": "^4.14.0", - "workerize-loader": "^1.0.1" + "workerize-loader": "^1.0.4" }, "dependencies": { - "babel-core": "^6.26.0", - "babel-loader": "^7.1.2", + "@babel/core": "^7.4.3", + "@babel/plugin-proposal-object-rest-spread": "^7.4.3", + "@babel/plugin-transform-react-jsx": "^7.3.0", + "@babel/polyfill": "^7.4.3", + "@babel/preset-env": "^7.4.3", + "@babel/preset-stage-0": "^7.0.0", + "babel-loader": "^8.0.5", "babel-plugin-istanbul": "^5.1.0", - "babel-plugin-transform-object-rest-spread": "^6.26.0", - "babel-plugin-transform-react-jsx": "^6.24.1", - "babel-polyfill": "^6.26.0", - "babel-preset-env": "^1.6.1", - "babel-preset-stage-0": "^6.24.1", "chalk": "^2.3.0", "dlv": "^1.1.1", "jasmine-core": "^3.3.0", "karma": "^3.1.1", "karma-chrome-launcher": "^2.2.0", "karma-coverage": "^1.1.2", + "karma-firefox-launcher": "^1.1.0", "karma-jasmine": "^2.0.1", + "karma-sauce-launcher": "^2.0.2", "karma-sourcemap-loader": "^0.3.7", "karma-spec-reporter": "0.0.32", "karma-webpack": "2.0.7", diff --git a/src/cli.js b/src/cli.js index 6a02a04..957700b 100644 --- a/src/cli.js +++ b/src/cli.js @@ -6,9 +6,10 @@ import './lib/patch'; import karmatic from '.'; import { cleanStack } from './lib/util'; +// @ts-ignore const { version } = require('../package.json'); -let toArray = val => Array.isArray(val) ? val : val == null ? [] : [val]; +let toArray = val => typeof val === 'string' ? val.split(/\s*,\s*/) : val == null ? [] : [].concat(val); let prog = sade('karmatic'); @@ -32,6 +33,7 @@ prog .command('debug [...files]') .describe('Open a headful Puppeteer instance for debugging your tests') .option('--headless', 'Run using Chrome Headless', false) // Override default to false + .option('--browsers', 'Run in specific browsers', null) .option('--coverage', 'Report code coverage of tests', false) // Override default to false .action( (str, opts) => run(str, opts, true) ); @@ -40,6 +42,8 @@ prog.parse(process.argv); function run(str, opts, isWatch) { opts.watch = !!isWatch; opts.files = toArray(str || opts.files).concat(opts._); + const b = opts.browsers || opts.browser; + opts.browsers = b ? toArray(b) : null; karmatic(opts) .then( output => { if (output!=null) process.stdout.write(output + '\n'); diff --git a/src/configure.js b/src/configure.js index 2e4818a..88e2e69 100644 --- a/src/configure.js +++ b/src/configure.js @@ -1,13 +1,23 @@ import path from 'path'; import puppeteer from 'puppeteer'; +import chalk from 'chalk'; import delve from 'dlv'; -import { moduleDir, tryRequire, dedupe, cleanStack, readFile, readDir } from './lib/util'; +import { tryRequire, dedupe, cleanStack, readFile, readDir } from './lib/util'; import babelLoader from './lib/babel-loader'; import cssLoader from './lib/css-loader'; const WEBPACK_VERSION = String(require('webpack').version || '3.0.0'); -const WEBPACK_MAJOR = WEBPACK_VERSION.split('.')[0]|0; +const WEBPACK_MAJOR = parseInt(WEBPACK_VERSION.split('.')[0], 10); +/** + * @param {Object} options + * @param {Array} options.files - Test files to run + * @param {Array} [options.browsers] - Custom list of browsers to run in + * @param {Boolean} [options.headless=false] - Run in Headless Chrome? + * @param {Boolean} [options.watch=false] - Start a continuous test server and retest when files change + * @param {Boolean} [options.coverage=false] - Instrument and collect code coverage statistics + * @param {Object} [options.webpackConfig] - Custom webpack configuration + */ export default function configure(options) { let cwd = process.cwd(), res = file => path.resolve(cwd, file); @@ -17,7 +27,9 @@ export default function configure(options) { process.env.CHROME_BIN = puppeteer.executablePath(); - let gitignore = (readFile(path.resolve(cwd, '.gitignore'), 'utf8') || '').replace(/(^\s*|\s*$|#.*$)/g, '').split('\n').filter(Boolean); + let gitignore = ( + readFile(path.resolve(cwd, '.gitignore')) || '' + ).replace(/(^\s*|\s*$|#.*$)/g, '').split('\n').filter(Boolean); let repoRoot = (readDir(cwd) || []).filter( c => c[0]!=='.' && c!=='node_modules' && gitignore.indexOf(c)===-1 ); let rootFiles = '{' + repoRoot.join(',') + '}'; @@ -31,6 +43,56 @@ export default function configure(options) { options.coverage ? 'karma-coverage' : [] ); + // Custom launchers to be injected: + const launchers = {}; + let useSauceLabs = false; + + let browsers; + if (options.browsers) { + browsers = options.browsers.map(browser => { + if (/^chrome$/i.test(browser)) { + return 'Chrome'; + } + if (/^firefox$/i.test(browser)) { + PLUGINS.push('karma-firefox-launcher'); + return 'Firefox'; + } + if (/^sauce-/.test(browser)) { + if (!useSauceLabs) { + useSauceLabs = true; + PLUGINS.push('karma-sauce-launcher'); + } + const parts = browser.toLowerCase().split('-'); + const name = parts.join('_'); + launchers[name] = { + base: 'SauceLabs', + browserName: parts[1].replace(/^ie$/gi, 'Internet Explorer'), + version: parts[2] || undefined, + platform: parts[3] ? parts[3].replace(/^win(dows)?[ -]+/gi, 'Windows ').replace(/^(macos|mac ?os ?x|os ?x)[ -]+/gi, 'OS X ') : undefined + }; + return name; + } + return browser; + }); + } + else { + browsers = [options.headless===false ? 'KarmaticChrome' : 'KarmaticChromeHeadless']; + } + + if (useSauceLabs) { + let missing = ['SAUCE_USERNAME', 'SAUCE_ACCESS_KEY'].filter(x => !process.env[x])[0]; + if (missing) { + throw ( + '\n' + + chalk.bold.bgRed.white('Error:') + ' Missing SauceLabs auth configuration.' + + '\n ' + chalk.white(`A SauceLabs browser was requested, but no ${chalk.magentaBright(missing)} environment variable provided.`) + + '\n ' + chalk.white('Try prepending it to your test command:') + + ' ' + chalk.greenBright(missing + '=... npm test') + + '\n' + ); + } + } + const WEBPACK_CONFIGS = [ 'webpack.config.babel.js', 'webpack.config.js' @@ -109,22 +171,27 @@ export default function configure(options) { let generatedConfig = { basePath: cwd, - plugins: PLUGINS.map(require.resolve), + plugins: PLUGINS.map(req => require.resolve(req)), frameworks: ['jasmine'], reporters: ['spec'].concat( - options.coverage ? 'coverage' : [] + options.coverage ? 'coverage' : [], + useSauceLabs ? 'saucelabs' : [] ), - browsers: [options.headless===false ? 'KarmaticChrome' : 'KarmaticChromeHeadless'], + browsers, + sauceLabs: { + testName: pkg && pkg.name || undefined + }, - customLaunchers: { + customLaunchers: Object.assign({ KarmaticChrome: { - base: 'Chrome' + base: 'Chrome', + flags: ['--no-sandbox'] }, KarmaticChromeHeadless: { base: 'ChromeHeadless', flags: ['--no-sandbox'] } - }, + }, launchers), coverageReporter: { reporters: [ @@ -149,7 +216,8 @@ export default function configure(options) { }], files: [ - { pattern: moduleDir('babel-polyfill')+'/dist/polyfill.js', watched: false, included: true, served: true } + // @TODO remove me + // { pattern: moduleDir('babel-polyfill')+'/dist/polyfill.js', watched: false, included: true, served: true } ].concat( ...files.map( pattern => { // Expand '**/xx' patterns but exempt node_modules and gitignored directories let matches = pattern.match(/^\*\*\/(.+)$/); diff --git a/src/lib/babel-loader.js b/src/lib/babel-loader.js index 8129497..0142350 100644 --- a/src/lib/babel-loader.js +++ b/src/lib/babel-loader.js @@ -2,23 +2,33 @@ export default function babelLoader(options) { return { test: /\.jsx?$/, exclude: /node_modules/, - loader: 'babel-loader', + loader: require.resolve('babel-loader'), query: { presets: [ - [require.resolve('babel-preset-env'), { + [require.resolve('@babel/preset-env'), { targets: { - browsers: 'last 2 Chrome versions' + browsers: [ + 'last 2 Chrome versions', + 'last 2 Firefox versions', + options.browsers && String(options.browsers).match(/(\bie(\b|\d)|internet.explorer)/gi) && 'ie>=9' + ].filter(Boolean) }, + corejs: 2, + useBuiltIns: 'usage', modules: false, loose: true - }], - require.resolve('babel-preset-stage-0') + }] ], plugins: [ - [require.resolve('babel-plugin-transform-object-rest-spread')], - [require.resolve('babel-plugin-transform-react-jsx'), { pragma: options.pragma || 'h' }] + [require.resolve('@babel/plugin-proposal-object-rest-spread'), { + loose: true, + useBuiltIns: true + }], + [require.resolve('@babel/plugin-transform-react-jsx'), { + pragma: options.pragma || 'h' + }] ].concat( - options.coverage ? require.resolve('babel-plugin-istanbul') : [] + options.coverage ? [require.resolve('babel-plugin-istanbul')] : [] ) } }; diff --git a/src/lib/css-loader.js b/src/lib/css-loader.js index 5e7929e..0d8987a 100644 --- a/src/lib/css-loader.js +++ b/src/lib/css-loader.js @@ -1,4 +1,4 @@ -export default function cssLoader() { +export default function cssLoader(options) { return { test: /\.css$/, loader: 'style-loader!css-loader'