From 4eca80bc9f1039d6159c0fd112a465cbbd600739 Mon Sep 17 00:00:00 2001 From: Wil Wilsman Date: Tue, 2 Feb 2021 11:48:25 -0600 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Backwards=20compatibility=20with=20?= =?UTF-8?q?v1=20config=20(#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ Add extensible config migration * 🔥 Remove migration logic from config:migrate * ✨ Handle own snapshot config migration logic * ✨ Handle own upload config migration logic * ✅ Move core queue tests to unit directory * ✨ Handle own core config migration logic --- .../cli-config/src/commands/config/migrate.js | 71 ++----------------- packages/cli-config/test/migrate.test.js | 59 ++++----------- packages/cli-snapshot/package.json | 2 +- packages/cli-snapshot/src/config.js | 13 ++++ packages/cli-snapshot/src/hooks/init.js | 3 +- .../cli-snapshot/test/unit/config.test.js | 54 ++++++++++++++ packages/cli-upload/package.json | 2 +- packages/cli-upload/src/config.js | 11 +++ packages/cli-upload/src/hooks/init.js | 3 +- packages/cli-upload/test/unit/config.test.js | 52 ++++++++++++++ packages/config/src/index.js | 4 ++ packages/config/src/load.js | 27 ++++--- packages/config/src/migrate.js | 38 ++++++++++ packages/config/test/index.test.js | 65 +++++++++++++++-- packages/core/package.json | 2 +- packages/core/src/config.js | 25 +++++++ packages/core/src/index.js | 8 +++ packages/core/src/percy.js | 4 -- packages/core/test/unit/config.test.js | 63 ++++++++++++++++ packages/core/test/{ => unit}/queue.test.js | 4 +- 20 files changed, 371 insertions(+), 139 deletions(-) create mode 100644 packages/cli-snapshot/test/unit/config.test.js create mode 100644 packages/cli-upload/test/unit/config.test.js create mode 100644 packages/config/src/migrate.js create mode 100644 packages/core/test/unit/config.test.js rename packages/core/test/{ => unit}/queue.test.js (97%) diff --git a/packages/cli-config/src/commands/config/migrate.js b/packages/cli-config/src/commands/config/migrate.js index 2d829d34d..5b6b86337 100644 --- a/packages/cli-config/src/commands/config/migrate.js +++ b/packages/cli-config/src/commands/config/migrate.js @@ -5,10 +5,6 @@ import Command, { flags } from '@oclif/command'; import PercyConfig from '@percy/config'; import logger from '@percy/logger'; -function assignOrCreate(obj, key, value) { - return Object.assign(obj || {}, { [key]: value }); -} - export class Migrate extends Command { static description = 'Migrate a Percy config file to the latest version'; @@ -44,8 +40,6 @@ export class Migrate extends Command { flags: { 'dry-run': dry } } = this.parse(); - logger.loglevel('info'); - // load config using the explorer directly rather than the load method to // better control logs and prevent validation try { @@ -77,13 +71,14 @@ export class Migrate extends Command { // migrate config this.log.info('Migrating config file...'); let format = path.extname(output).replace(/^./, '') || 'yaml'; - config = PercyConfig.stringify(format, this.migrate(config)); + let migrated = PercyConfig.migrate(config); + let body = PercyConfig.stringify(format, migrated); // update the package.json entry via string replacement if (!dry && path.basename(output) === 'package.json') { fs.writeFileSync(output, fs.readFileSync(output).replace( /(\s+)("percy":\s*){.*\1}/s, - `$1$2${config.replace(/\n/g, '$$1')}` + `$1$2${body.replace(/\n/g, '$$1')}` )); // write to output } else if (!dry) { @@ -94,67 +89,11 @@ export class Migrate extends Command { fs.renameSync(input, old); } - fs.writeFileSync(output, config); + fs.writeFileSync(output, body); } this.log.info('Config file migrated!'); // when dry-running, print config to stdout when finished - if (dry) logger.instance.stdout.write('\n' + config); - } - - // Migrating config options is recursive so no matter which input version is - // provided, the output will be the latest version. - migrate(input) { - switch (input.version) { - case 2: return input; // latest version - default: return this.migrate(this.v1(input)); - } - } - - // Migrate config from v1 to v2. - /* eslint-disable curly */ - v1(input) { - let output = { version: 2 }; - - // previous snapshot options map 1:1 - if (input.snapshot != null) - output.snapshot = input.snapshot; - // request-headers option moved - if (input.agent?.['asset-discovery']?.['request-headers'] != null) - output.snapshot = assignOrCreate(output.snapshot, 'request-headers', ( - input.agent['asset-discovery']['request-headers'])); - // only create discovery options when neccessary - if (input.agent?.['asset-discovery']?.['allowed-hostnames'] != null) - output.discovery = assignOrCreate(output.discovery, 'allowed-hostnames', ( - input.agent['asset-discovery']['allowed-hostnames'])); - if (input.agent?.['asset-discovery']?.['network-idle-timeout'] != null) - output.discovery = assignOrCreate(output.discovery, 'network-idle-timeout', ( - input.agent['asset-discovery']['network-idle-timeout'])); - // page pooling was rewritten to be a concurrent task queue - if (input.agent?.['asset-discovery']?.['page-pool-size-max'] != null) - output.discovery = assignOrCreate(output.discovery, 'concurrency', ( - input.agent['asset-discovery']['page-pool-size-max'])); - // cache-responses was renamed to match the CLI flag - if (input.agent?.['asset-discovery']?.['cache-responses'] != null) - output.discovery = assignOrCreate(output.discovery, 'disable-cache', ( - !input.agent['asset-discovery']['cache-responses'])); - // image-snapshots was renamed - if (input['image-snapshots'] != null) - output.upload = input['image-snapshots']; - // image-snapshots path was removed - if (output.upload?.path != null) - delete output.upload.path; - // static-snapshots and options were renamed - if (input['static-snapshots']?.['base-url'] != null) - output.static = assignOrCreate(output.static, 'base-url', ( - input['static-snapshots']['base-url'])); - if (input['static-snapshots']?.['snapshot-files'] != null) - output.static = assignOrCreate(output.static, 'files', ( - input['static-snapshots']['snapshot-files'])); - if (input['static-snapshots']?.['ignore-files'] != null) - output.static = assignOrCreate(output.static, 'ignore', ( - input['static-snapshots']['ignore-files'])); - - return output; + if (dry) logger.instance.stdout.write('\n' + body); } } diff --git a/packages/cli-config/test/migrate.test.js b/packages/cli-config/test/migrate.test.js index 629d38eca..c51477b8e 100644 --- a/packages/cli-config/test/migrate.test.js +++ b/packages/cli-config/test/migrate.test.js @@ -1,10 +1,18 @@ import expect from 'expect'; +import PercyConfig from '@percy/config'; import { logger, mockConfig, getMockConfig } from './helpers'; import { Migrate } from '../src/commands/config/migrate'; describe('percy config:migrate', () => { beforeEach(() => { mockConfig('.percy.yml', 'version: 1\n'); + PercyConfig.addMigration((input, set) => { + if (input.migrate != null) set('migrated', input.migrate.replace('old', 'new')); + }); + }); + + afterEach(() => { + PercyConfig.clearMigrations(); }); it('by default, renames the config before writing', async () => { @@ -106,60 +114,17 @@ describe('percy config:migrate', () => { expect(getMockConfig('.percy.old.yml')).toBeUndefined(); }); - it('migrates v1 config', async () => { + it('runs registered migrations on the config', async () => { mockConfig('.percy.yml', [ 'version: 1', - 'snapshot:', - ' widths: [1000]', - ' min-height: 1000', - ' enable-javascript: true', - ' percy-css: "iframe { display: none; }"', - 'agent:', - ' asset-discovery:', - ' allowed-hostnames:', - ' - cdn.example.com', - ' request-headers:', - ' Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=', - ' network-idle-timeout: 500', - ' cache-responses: false', - ' page-pool-size-min: 10', - ' page-pool-size-max: 50', - 'static-snapshots:', - ' path: _site/', - ' base-url: /blog/', - ' snapshot-files: "**/*.html"', - ' ignore-files: "**/*.htm"', - 'image-snapshots:', - ' path: _images/', - ' files: "**/*.html"', - ' ignore: "**/*.htm"\n' + 'migrate: old-value' ].join('\n')); await Migrate.run([]); expect(getMockConfig('.percy.yml')).toEqual([ 'version: 2', - 'snapshot:', - ' widths:', - ' - 1000', - ' min-height: 1000', - ' enable-javascript: true', - ' percy-css: "iframe { display: none; }"', - ' request-headers:', - ' Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=', - 'discovery:', - ' allowed-hostnames:', - ' - cdn.example.com', - ' network-idle-timeout: 500', - ' concurrency: 50', - ' disable-cache: true', - 'upload:', - ' files: "**/*.html"', - ' ignore: "**/*.htm"', - 'static:', - ' base-url: /blog/', - ' files: "**/*.html"', - ' ignore: "**/*.htm"\n' - ].join('\n')); + 'migrated: new-value' + ].join('\n') + '\n'); }); }); diff --git a/packages/cli-snapshot/package.json b/packages/cli-snapshot/package.json index 425272823..fe99d746c 100644 --- a/packages/cli-snapshot/package.json +++ b/packages/cli-snapshot/package.json @@ -13,7 +13,7 @@ "postbuild": "oclif-dev manifest", "readme": "oclif-dev readme", "pretest": "node ../../scripts/install-browser", - "test": "cross-env NODE_ENV=test mocha", + "test": "cross-env NODE_ENV=test mocha --recursive", "test:coverage": "nyc yarn test" }, "publishConfig": { diff --git a/packages/cli-snapshot/src/config.js b/packages/cli-snapshot/src/config.js index 09753525a..c1a620e74 100644 --- a/packages/cli-snapshot/src/config.js +++ b/packages/cli-snapshot/src/config.js @@ -24,3 +24,16 @@ export const schema = { } } }; + +export function migration(input, set) { + /* eslint-disable curly */ + if (input.version < 2) { + // static-snapshots and options were renamed + if (input.staticSnapshots?.baseUrl != null) + set('static.baseUrl', input.staticSnapshots.baseUrl); + if (input.staticSnapshots?.snapshotFiles != null) + set('static.files', input.staticSnapshots.snapshotFiles); + if (input.staticSnapshots?.ignoreFiles != null) + set('static.ignore', input.staticSnapshots.ignoreFiles); + } +} diff --git a/packages/cli-snapshot/src/hooks/init.js b/packages/cli-snapshot/src/hooks/init.js index e832029c6..f04628355 100644 --- a/packages/cli-snapshot/src/hooks/init.js +++ b/packages/cli-snapshot/src/hooks/init.js @@ -1,6 +1,7 @@ import PercyConfig from '@percy/config'; -import { schema } from '../config'; +import { schema, migration } from '../config'; export default function() { PercyConfig.addSchema(schema); + PercyConfig.addMigration(migration); } diff --git a/packages/cli-snapshot/test/unit/config.test.js b/packages/cli-snapshot/test/unit/config.test.js new file mode 100644 index 000000000..66f8ec2fa --- /dev/null +++ b/packages/cli-snapshot/test/unit/config.test.js @@ -0,0 +1,54 @@ +import expect from 'expect'; +import { migration } from '../../src/config'; + +describe('unit / config', () => { + let migrate; + let set = (key, value) => (migrate[key] = value); + + beforeEach(() => { + migrate = {}; + }); + + it('migrates v1 config', () => { + migration({ + version: 1, + staticSnapshots: { + baseUrl: 'base-url', + snapshotFiles: '*.html', + ignoreFiles: '*.htm' + } + }, set); + + expect(migrate).toEqual({ + 'static.baseUrl': 'base-url', + 'static.files': '*.html', + 'static.ignore': '*.htm' + }); + }); + + it('only migrates own config options', () => { + migration({ + version: 1, + otherOptions: { + baseUrl: 'base-url', + snapshotFiles: '*.html', + ignoreFiles: '*.htm' + } + }, set); + + expect(migrate).toEqual({}); + }); + + it('does not migrate when not needed', () => { + migration({ + version: 2, + static: { + baseUrl: 'base-url', + files: '*.html', + ignore: '*.htm' + } + }, set); + + expect(migrate).toEqual({}); + }); +}); diff --git a/packages/cli-upload/package.json b/packages/cli-upload/package.json index 7f8ceeb49..e28622f82 100644 --- a/packages/cli-upload/package.json +++ b/packages/cli-upload/package.json @@ -12,7 +12,7 @@ "lint": "eslint --ignore-path ../../.gitignore .", "postbuild": "oclif-dev manifest", "readme": "oclif-dev readme", - "test": "cross-env NODE_ENV=test mocha", + "test": "cross-env NODE_ENV=test mocha --recursive", "test:coverage": "nyc yarn test" }, "publishConfig": { diff --git a/packages/cli-upload/src/config.js b/packages/cli-upload/src/config.js index 54546204b..503f7134b 100644 --- a/packages/cli-upload/src/config.js +++ b/packages/cli-upload/src/config.js @@ -20,3 +20,14 @@ export const schema = { } } }; + +export function migration(input, set) { + /* eslint-disable curly */ + if (input.version < 2) { + // image-snapshots and options were renamed + if (input.imageSnapshots?.files != null) + set('upload.files', input.imageSnapshots.files); + if (input.imageSnapshots?.ignore != null) + set('upload.ignore', input.imageSnapshots.ignore); + } +} diff --git a/packages/cli-upload/src/hooks/init.js b/packages/cli-upload/src/hooks/init.js index e832029c6..f04628355 100644 --- a/packages/cli-upload/src/hooks/init.js +++ b/packages/cli-upload/src/hooks/init.js @@ -1,6 +1,7 @@ import PercyConfig from '@percy/config'; -import { schema } from '../config'; +import { schema, migration } from '../config'; export default function() { PercyConfig.addSchema(schema); + PercyConfig.addMigration(migration); } diff --git a/packages/cli-upload/test/unit/config.test.js b/packages/cli-upload/test/unit/config.test.js new file mode 100644 index 000000000..d9dab17e5 --- /dev/null +++ b/packages/cli-upload/test/unit/config.test.js @@ -0,0 +1,52 @@ +import expect from 'expect'; +import { migration } from '../../src/config'; + +describe('unit / config', () => { + let migrate; + let set = (key, value) => (migrate[key] = value); + + beforeEach(() => { + migrate = {}; + }); + + it('migrates v1 config', () => { + migration({ + version: 1, + imageSnapshots: { + path: '~/pathname/', + files: '*.png', + ignore: '*.jpg' + } + }, set); + + expect(migrate).toEqual({ + 'upload.files': '*.png', + 'upload.ignore': '*.jpg' + }); + }); + + it('only migrates own config options', () => { + migration({ + version: 1, + otherOptions: { + path: '~/pathname/', + files: '*.png', + ignore: '*.jpg' + } + }, set); + + expect(migrate).toEqual({}); + }); + + it('does not migrate when not needed', () => { + migration({ + version: 2, + upload: { + files: '*.png', + ignore: '*.jpg' + } + }, set); + + expect(migrate).toEqual({}); + }); +}); diff --git a/packages/config/src/index.js b/packages/config/src/index.js index f6a53b104..73b454c09 100644 --- a/packages/config/src/index.js +++ b/packages/config/src/index.js @@ -1,5 +1,6 @@ import load, { cache, explorer } from './load'; import validate, { addSchema, resetSchema } from './validate'; +import migrate, { addMigration, clearMigrations } from './migrate'; import getDefaults from './defaults'; import stringify from './stringify'; @@ -11,6 +12,9 @@ export default { validate, addSchema, resetSchema, + migrate, + addMigration, + clearMigrations, getDefaults, stringify }; diff --git a/packages/config/src/load.js b/packages/config/src/load.js index 97c23b4c9..5b626bb75 100644 --- a/packages/config/src/load.js +++ b/packages/config/src/load.js @@ -3,9 +3,10 @@ import { cosmiconfigSync } from 'cosmiconfig'; import { isDirectorySync } from 'path-type'; import logger from '@percy/logger'; import getDefaults from './defaults'; +import migrate from './migrate'; import normalize from './normalize'; -import validate from './validate'; import { inspect } from './stringify'; +import validate from './validate'; // Loaded configuration file cache export const cache = new Map(); @@ -30,8 +31,8 @@ export const explorer = cosmiconfigSync('percy', { // unless `reload` is true in which the file will be reloaded and the cache // updated. Validation errors are logged as warnings and the config is returned // unless `bail` is true. Supports kebab-case and camelCase config options and -// always returns camelCase options. Currently only supports version 2 config -// files; missing versions or other versions are discarded. +// always returns camelCase options. Will automatically convert older config +// versions to the latest version while printing a warning. export default function load({ path, overrides = {}, @@ -53,15 +54,21 @@ export default function load({ if (result && result.config) { log[infoDebug](`Found config file: ${relative('', result.filepath)}`); + let version = parseInt(result.config.version, 10); - if (result.config.version !== 2) { - log.warn('Ignoring config file - ' + ( - !result.config.version - ? 'missing version' - : 'unsupported version' - )); + if (Number.isNaN(version)) { + log.warn('Ignoring config file - missing or invalid version'); + } else if (version > 2) { + log.warn(`Ignoring config file - unsupported version "${version}"`); } else { - config = result.config; + if (version < 2) { + log.warn('Found older config file version, please run ' + ( + '`percy config:migrate` to update to the latest version')); + config = migrate(result.config); + } else { + config = result.config; + } + cache.set(path, config); } } else { diff --git a/packages/config/src/migrate.js b/packages/config/src/migrate.js new file mode 100644 index 000000000..0d5ace634 --- /dev/null +++ b/packages/config/src/migrate.js @@ -0,0 +1,38 @@ +import normalize from './normalize'; + +// Global set of registered migrations +const migrations = new Set(); + +// Register a migration function +export function addMigration(migration) { + migrations.add(migration); +} + +// Clear all migration functions +export function clearMigrations() { + migrations.clear(); +} + +// Assigns a value to the object at the path creating any necessary nested +// objects along the way +function assign(obj, path, value) { + path.split('.').reduce((loc, key, i, a) => ( + loc[key] = i === a.length - 1 ? value : (loc[key] || {}) + ), obj); + + return obj; +} + +// Calls each registered migration function with a normalize provided config +// and a `set` function which assigns values to the returned output +export default function migrate(config) { + let output = { version: 2 }; + let input = normalize(config); + let set = assign.bind(null, output); + + migrations.forEach(migration => { + migration(input, set); + }); + + return output; +} diff --git a/packages/config/test/index.test.js b/packages/config/test/index.test.js index cf96281ca..5e967e543 100644 --- a/packages/config/test/index.test.js +++ b/packages/config/test/index.test.js @@ -23,6 +23,7 @@ describe('PercyConfig', () => { afterEach(() => { PercyConfig.cache.clear(); PercyConfig.resetSchema(); + PercyConfig.clearMigrations(); }); describe('.addSchema()', () => { @@ -137,6 +138,30 @@ describe('PercyConfig', () => { }); }); + describe('.migrate()', () => { + beforeEach(() => { + PercyConfig.addMigration((input, set) => { + if (input.test != null) set('value', input.test); + }); + + PercyConfig.addMigration((input, set) => { + if (input.foo != null) set('foo.bar', input.foo); + }); + }); + + it('runs registered migration functions', () => { + expect(PercyConfig.migrate({ + version: 1, + test: 'testing', + foo: 'baz' + })).toEqual({ + version: 2, + value: 'testing', + foo: { bar: 'baz' } + }); + }); + }); + describe('.load()', () => { beforeEach(() => { PercyConfig.addSchema({ @@ -336,7 +361,7 @@ describe('PercyConfig', () => { ]); }); - it('logs with a missing version and uses default options', () => { + it('warns with a missing version and uses default options', () => { mockConfig('.no-version.yml', 'test:\n value: no-version'); logger.loglevel('debug'); @@ -349,12 +374,12 @@ describe('PercyConfig', () => { expect(logger.stdout).toEqual([]); expect(logger.stderr).toEqual([ '[percy:config] Found config file: .no-version.yml\n', - '[percy:config] Ignoring config file - missing version\n' + '[percy:config] Ignoring config file - missing or invalid version\n' ]); }); - it('logs with an invalid version and uses default options', () => { - mockConfig('.bad-version.yml', 'version: 1\ntest:\n value: bad-version'); + it('warns with an unsupported version and uses default options', () => { + mockConfig('.bad-version.yml', 'version: 3\ntest:\n value: bad-version'); logger.loglevel('debug'); expect(PercyConfig.load({ path: '.bad-version.yml' })) @@ -366,7 +391,37 @@ describe('PercyConfig', () => { expect(logger.stdout).toEqual([]); expect(logger.stderr).toEqual([ '[percy:config] Found config file: .bad-version.yml\n', - '[percy:config] Ignoring config file - unsupported version\n' + '[percy:config] Ignoring config file - unsupported version "3"\n' + ]); + }); + + it('warns with an older version and uses migrated options', () => { + mockConfig('.old-version.yml', 'version: 1\nvalue: old-value'); + logger.loglevel('debug'); + + PercyConfig.addMigration((input, set) => { + set('test.value', input.value.replace('old', 'new')); + }); + + expect(PercyConfig.load({ path: '.old-version.yml' })) + .toEqual({ + version: 2, + test: { value: 'new-value' } + }); + + expect(logger.stdout).toEqual([]); + expect(logger.stderr).toEqual([ + '[percy:config] Found config file: .old-version.yml\n', + '[percy:config] Found older config file version, please run ' + + '`percy config:migrate` to update to the latest version\n', + '[percy:config] Using config:\n' + [ + '{', + ' version: 2,', + ' test: {', + ' value: \'new-value\'', + ' }', + '}\n' + ].join('\n') ]); }); diff --git a/packages/core/package.json b/packages/core/package.json index 0dfb8d272..843a210e3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -13,7 +13,7 @@ "build": "babel --root-mode upward src --out-dir dist", "lint": "eslint --ignore-path ../../.gitignore .", "pretest": "node ../../scripts/install-browser", - "test": "cross-env NODE_ENV=test mocha", + "test": "cross-env NODE_ENV=test mocha --recursive", "test:coverage": "nyc yarn test", "test:types": "tsd" }, diff --git a/packages/core/src/config.js b/packages/core/src/config.js index b29739136..7962a15cf 100644 --- a/packages/core/src/config.js +++ b/packages/core/src/config.js @@ -67,3 +67,28 @@ export const schema = { } } }; + +// Migration function +export function migration(input, set) { + /* eslint-disable curly */ + if (input.version < 2) { + // previous snapshot options map 1:1 + if (input.snapshot != null) + set('snapshot', input.snapshot); + // request-headers option moved + if (input.agent?.assetDiscovery?.requestHeaders != null) + set('snapshot.requestHeaders', input.agent.assetDiscovery.requestHeaders); + // allowed-hostnames moved + if (input.agent?.assetDiscovery?.allowedHostnames != null) + set('discovery.allowedHostnames', input.agent.assetDiscovery.allowedHostnames); + // network-idle-timeout moved + if (input.agent?.assetDiscovery?.networkIdleTimeout != null) + set('discovery.networkIdleTimeout', input.agent.assetDiscovery.networkIdleTimeout); + // page pooling was rewritten to be a concurrent task queue + if (input.agent?.assetDiscovery?.pagePoolSizeMax != null) + set('discovery.concurrency', input.agent.assetDiscovery.pagePoolSizeMax); + // cache-responses was renamed to match the CLI flag + if (input.agent?.assetDiscovery?.cacheResponses != null) + set('discovery.disableCache', !input.agent.assetDiscovery.cacheResponses); + } +} diff --git a/packages/core/src/index.js b/packages/core/src/index.js index 115623c60..85e5b0003 100644 --- a/packages/core/src/index.js +++ b/packages/core/src/index.js @@ -1 +1,9 @@ +// Register core config options +const { default: PercyConfig } = require('@percy/config'); +const CoreConfig = require('./config'); + +PercyConfig.addSchema(CoreConfig.schema); +PercyConfig.addMigration(CoreConfig.migration); + +// Export the Percy class with commonjs compatibility module.exports = require('./percy').default; diff --git a/packages/core/src/percy.js b/packages/core/src/percy.js index c7463d0e8..df3e6d629 100644 --- a/packages/core/src/percy.js +++ b/packages/core/src/percy.js @@ -2,7 +2,6 @@ import { promises as fs } from 'fs'; import PercyClient from '@percy/client'; import PercyConfig from '@percy/config'; import logger from '@percy/logger'; -import { schema } from './config'; import Queue from './queue'; import Discoverer from './discovery/discoverer'; import createPercyServer from './server'; @@ -12,9 +11,6 @@ import injectPercyCSS from './utils/percy-css'; import { createRootResource, createLogResource } from './utils/resources'; import { normalizeURL } from './utils/url'; -// Register core config options -PercyConfig.addSchema(schema); - // A Percy instance will create a new build when started, handle snapshot // creation, asset discovery, and resource uploads, and will finalize the build // when stopped. Snapshots are processed concurrently and the build is not diff --git a/packages/core/test/unit/config.test.js b/packages/core/test/unit/config.test.js new file mode 100644 index 000000000..15678b7c2 --- /dev/null +++ b/packages/core/test/unit/config.test.js @@ -0,0 +1,63 @@ +import expect from 'expect'; +import { migration } from '../../src/config'; + +describe('Unit / Config', () => { + let migrate; + let set = (key, value) => (migrate[key] = value); + + beforeEach(() => { + migrate = {}; + }); + + it('migrates v1 config', () => { + migration({ + version: 1, + snapshot: { + widths: [1000] + }, + agent: { + assetDiscovery: { + requestHeaders: { foo: 'bar' }, + allowedHostnames: ['allowed'], + networkIdleTimeout: 150, + pagePoolSizeMin: 1, + pagePoolSizeMax: 5, + cacheResponses: false + } + } + }, set); + + expect(migrate).toEqual({ + snapshot: { widths: [1000] }, + 'snapshot.requestHeaders': { foo: 'bar' }, + 'discovery.allowedHostnames': ['allowed'], + 'discovery.networkIdleTimeout': 150, + 'discovery.concurrency': 5, + 'discovery.disableCache': true + }); + }); + + it('only migrates own config options', () => { + migration({ + version: 1, + otherOptions: { + baseUrl: 'base-url', + snapshotFiles: '*.html', + ignoreFiles: '*.htm' + } + }, set); + + expect(migrate).toEqual({}); + }); + + it('does not migrate when not needed', () => { + migration({ + version: 2, + discovery: { + allowedHostnames: ['allowed'] + } + }, set); + + expect(migrate).toEqual({}); + }); +}); diff --git a/packages/core/test/queue.test.js b/packages/core/test/unit/queue.test.js similarity index 97% rename from packages/core/test/queue.test.js rename to packages/core/test/unit/queue.test.js index d9553b093..feed5b1b3 100644 --- a/packages/core/test/queue.test.js +++ b/packages/core/test/unit/queue.test.js @@ -1,5 +1,5 @@ import expect from 'expect'; -import Queue from '../src/queue'; +import Queue from '../../src/queue'; function task(timeout = 0, cb) { return async function t() { @@ -11,7 +11,7 @@ function task(timeout = 0, cb) { }; } -describe('Tasks Queue', () => { +describe('Unit / Tasks Queue', () => { let q; beforeEach(() => {