diff --git a/packages/client/src/client.js b/packages/client/src/client.js index 722c140d9..3a197aea0 100644 --- a/packages/client/src/client.js +++ b/packages/client/src/client.js @@ -58,6 +58,8 @@ export class PercyClient { Object.assign(this, { token, config: config || {}, apiUrl }); this.addClientInfo(clientInfo); this.addEnvironmentInfo(environmentInfo); + this.buildType = null; + this.screenshotFlow = null; } // Adds additional unique client info. diff --git a/packages/core/src/api.js b/packages/core/src/api.js index 569490bc9..abb3fa0e1 100644 --- a/packages/core/src/api.js +++ b/packages/core/src/api.js @@ -105,10 +105,10 @@ export function createPercyServer(percy, port) { .route('post', '/percy/comparison', async (req, res) => { let data; if (percy.syncMode(req.body)) { - const snapshotPromise = new Promise((resolve, reject) => percy.upload(req.body, { resolve, reject })); + const snapshotPromise = new Promise((resolve, reject) => percy.upload(req.body, { resolve, reject }, 'app')); data = await handleSyncJob(snapshotPromise, percy, 'comparison'); } else { - let upload = percy.upload(req.body); + let upload = percy.upload(req.body, null, 'app'); if (req.url.searchParams.has('await')) await upload; } @@ -139,10 +139,10 @@ export function createPercyServer(percy, port) { percyAutomateRequestHandler(req, percy); let comparisonData = await WebdriverUtils.automateScreenshot(req.body); if (percy.syncMode(comparisonData)) { - const snapshotPromise = new Promise((resolve, reject) => percy.upload(comparisonData, { resolve, reject })); + const snapshotPromise = new Promise((resolve, reject) => percy.upload(comparisonData, { resolve, reject }, 'automate')); data = await handleSyncJob(snapshotPromise, percy, 'comparison'); } else { - percy.upload(comparisonData); + percy.upload(comparisonData, null, 'automate'); } res.json(200, { success: true, data: data }); diff --git a/packages/core/src/percy.js b/packages/core/src/percy.js index 3aa5c4e22..3bfd782a6 100644 --- a/packages/core/src/percy.js +++ b/packages/core/src/percy.js @@ -332,7 +332,7 @@ export class Percy { } // Uploads one or more snapshots directly to the current Percy build - upload(options, callback = null) { + upload(options, callback = null, screenshotFlow = null) { if (this.readyState !== 1) { throw new Error('Not running'); } else if (Array.isArray(options)) { @@ -359,6 +359,7 @@ export class Percy { // add client & environment info this.client.addClientInfo(options.clientInfo); this.client.addEnvironmentInfo(options.environmentInfo); + this.client.screenshotFlow = screenshotFlow; // Sync CLI support, attached resolve, reject promise if (this.syncMode(options)) { diff --git a/packages/core/src/snapshot.js b/packages/core/src/snapshot.js index 520a12501..48f0b45b1 100644 --- a/packages/core/src/snapshot.js +++ b/packages/core/src/snapshot.js @@ -313,6 +313,7 @@ export function createSnapshotsQueue(percy) { let { data } = await percy.client.createBuild({ projectType: percy.projectType }); let url = data.attributes['web-url']; let number = data.attributes['build-number']; + percy.client.buildType = data.attributes?.type; Object.assign(build, { id: data.id, url, number }); // immediately run the queue if not delayed or deferred if (!percy.delayUploads && !percy.deferUploads) queue.run(); @@ -361,6 +362,11 @@ export function createSnapshotsQueue(percy) { .handle('task', async function*({ resources, ...snapshot }) { let { name, meta } = snapshot; + if (percy.client.screenshotFlow === 'automate' && percy.client.buildType !== 'automate') { + throw new Error(`Cannot run automate screenshots in ${percy.client.buildType} project. Please use automate project token`); + } else if (percy.client.screenshotFlow === 'app' && percy.client.buildType !== 'app') { + throw new Error(`Cannot run App Percy screenshots in ${percy.client.buildType} project. Please use App Percy project token`); + } // yield to evaluated snapshot resources snapshot.resources = typeof resources === 'function' ? yield* yieldTo(resources()) diff --git a/packages/core/test/api.test.js b/packages/core/test/api.test.js index 8e03ba58a..077310669 100644 --- a/packages/core/test/api.test.js +++ b/packages/core/test/api.test.js @@ -208,7 +208,9 @@ describe('API Server', () => { })); expect(percy.upload).toHaveBeenCalledOnceWith( - { 'test-me': true, me_too: true } + { 'test-me': true, me_too: true }, + null, + 'app' ); await expectAsync(test).toBePending(); @@ -376,7 +378,7 @@ describe('API Server', () => { } })); - expect(percy.upload).toHaveBeenCalledOnceWith(mockWebdriverUtilResponse); + expect(percy.upload).toHaveBeenCalledOnceWith(mockWebdriverUtilResponse, null, 'automate'); await expectAsync(test).toBePending(); resolve(); // no hanging promises }); @@ -433,7 +435,7 @@ describe('API Server', () => { })); expect(percy.client.getComparisonDetails).toHaveBeenCalled(); - expect(percy.upload).toHaveBeenCalledOnceWith({ sync: true }, jasmine.objectContaining({})); + expect(percy.upload).toHaveBeenCalledOnceWith({ sync: true }, jasmine.objectContaining({}), 'automate'); }); it('has a /events endpoint that calls #sendBuildEvents() async with provided options with clientInfo present', async () => { diff --git a/packages/core/test/snapshot.test.js b/packages/core/test/snapshot.test.js index 702fafd24..6072ee28a 100644 --- a/packages/core/test/snapshot.test.js +++ b/packages/core/test/snapshot.test.js @@ -1365,6 +1365,50 @@ describe('Snapshot', () => { }); }); + describe('invalid screenshot flow', () => { + it('should throw error if app percy build is ran using automate SDK', async () => { + await percy.stop(true); + await api.mock(); + + percy = await Percy.start({ + token: 'PERCY_TOKEN', + projectType: 'app' + }); + + percy.client.buildType = 'app'; + await percy.upload({ + name: 'Snapshot', + external_debug_url: 'localhost', + some_other_rand_prop: 'random value', + tag: { name: 'device', foobar: 'baz' }, + tiles: [{ content: 'foo' }, { content: 'vbv' }] + }, null, 'automate'); + + expect(logger.stderr).toEqual(jasmine.arrayContaining([jasmine.stringContaining('[percy] Error: Cannot run automate screenshots in app project. Please use automate project token')])); + }); + + it('should throw error if automate build is ran using app percy SDK', async () => { + await percy.stop(true); + await api.mock(); + + percy = await Percy.start({ + token: 'PERCY_TOKEN', + projectType: 'automate' + }); + + percy.client.buildType = 'automate'; + await percy.upload({ + name: 'Snapshot', + external_debug_url: 'localhost', + some_other_rand_prop: 'random value', + tag: { name: 'device', foobar: 'baz' }, + tiles: [{ content: 'foo' }, { content: 'vbv' }] + }, null, 'app'); + + expect(logger.stderr).toEqual(jasmine.arrayContaining([jasmine.stringContaining('[percy] Error: Cannot run App Percy screenshots in automate project. Please use App Percy project token')])); + }); + }); + describe('with percy-css', () => { let getResourceData = () => ( api.requests['/builds/123/snapshots'][0].body.data.relationships.resources.data