diff --git a/packages/core/src/percy.js b/packages/core/src/percy.js index cf31857bb..304348b9f 100644 --- a/packages/core/src/percy.js +++ b/packages/core/src/percy.js @@ -1,5 +1,6 @@ import PercyClient from '@percy/client'; import PercyConfig from '@percy/config'; +import { merge } from '@percy/config/dist/utils'; import logger from '@percy/logger'; import Queue from './queue'; import Browser from './browser'; @@ -94,6 +95,32 @@ export default class Percy { return `http://localhost:${this.port}`; } + // Set client & environment info, and override loaded config options + setConfig({ clientInfo, environmentInfo, ...config }) { + this.client.addClientInfo(clientInfo); + this.client.addEnvironmentInfo(environmentInfo); + + // normalize config and do nothing if empty + config = PercyConfig.normalize(config, { schema: '/config' }); + if (!config) return this.config; + + // validate provided config options + let errors = PercyConfig.validate(config); + + if (errors) { + this.log.warn('Invalid config:'); + for (let e of errors) this.log.warn(`- ${e.path}: ${e.message}`); + } + + // merge and override existing config options + this.config = merge([this.config, config], (path, prev, next) => { + // replace arrays instead of merging + return Array.isArray(next) && [path, next]; + }); + + return this.config; + } + // Resolves once snapshot and upload queues are idle async idle() { await this.#snapshots.idle(); diff --git a/packages/core/src/server.js b/packages/core/src/server.js index 80fcb597a..b5a1395c9 100644 --- a/packages/core/src/server.js +++ b/packages/core/src/server.js @@ -108,6 +108,12 @@ export default function createPercyServer(percy) { build: percy.build }], + // remotely get and set percy config options + '/percy/config': ({ body }) => [200, 'application/json', { + config: body ? percy.setConfig(body) : percy.config, + success: true + }], + // responds when idle '/percy/idle': () => percy.idle() .then(() => [200, 'application/json', { success: true }]), diff --git a/packages/core/test/percy.test.js b/packages/core/test/percy.test.js index fee8eb359..58aef0aae 100644 --- a/packages/core/test/percy.test.js +++ b/packages/core/test/percy.test.js @@ -136,6 +136,43 @@ describe('Percy', () => { }); }); + describe('#setConfig(config)', () => { + it('adds client and environment information', () => { + expect(percy.setConfig({ + clientInfo: 'client/info', + environmentInfo: 'env/info' + })).toEqual(percy.config); + + expect(percy.client.clientInfo).toContain('client/info'); + expect(percy.client.environmentInfo).toContain('env/info'); + }); + + it('merges existing and provided config options', () => { + expect(percy.setConfig({ + snapshot: { widths: [1000] } + })).toEqual({ + ...percy.config, + snapshot: { + ...percy.config.snapshot, + widths: [1000] + } + }); + }); + + it('warns and ignores invalid config options', () => { + expect(percy.setConfig({ + snapshot: { widths: 1000 }, + foo: 'bar' + })).toEqual(percy.config); + + expect(logger.stderr).toEqual([ + '[percy] Invalid config:', + '[percy] - foo: unknown property', + '[percy] - snapshot.widths: must be an array, received a number' + ]); + }); + }); + describe('#start()', () => { it('creates a new build', async () => { await expectAsync(percy.start()).toBeResolved(); diff --git a/packages/core/test/server.test.js b/packages/core/test/server.test.js index b247c05e1..894a83f53 100644 --- a/packages/core/test/server.test.js +++ b/packages/core/test/server.test.js @@ -49,6 +49,40 @@ describe('Server', () => { }); }); + it('has a /config endpoint that returns loaded config options', async () => { + await percy.start(); + + let response = await fetch('http://localhost:1337/percy/config'); + await expectAsync(response.json()).toBeResolvedTo({ + success: true, + config: PercyConfig.getDefaults() + }); + }); + + it('can set config options via the /config endpoint', async () => { + await percy.start(); + + let response = await fetch('http://localhost:1337/percy/config', { + method: 'POST', + body: JSON.stringify({ + snapshot: { widths: [1000] } + }) + }); + + await expectAsync(response.json()).toBeResolvedTo({ + success: true, + config: PercyConfig.getDefaults({ + snapshot: { widths: [1000] } + }) + }); + + expect(percy.config).toEqual( + PercyConfig.getDefaults({ + snapshot: { widths: [1000] } + }) + ); + }); + it('has an /idle endpoint that calls #idle()', async () => { spyOn(percy, 'idle').and.resolveTo(); await percy.start();