Skip to content

Commit

Permalink
✨ Accept multiple snapshots and an async API param (#576)
Browse files Browse the repository at this point in the history
* ✨ Add ability to handle server snapshots async

* ✨ Accept multiple snapshots at once in core API

* ✨ Accept params as second arg to sdk-util snapshot helper

* ♻ Utilize batch snapshot processing
  • Loading branch information
Wil Wilsman committed Oct 11, 2021
1 parent ceadd44 commit c6ff602
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 12 deletions.
5 changes: 1 addition & 4 deletions packages/cli-snapshot/src/commands/snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,7 @@ export class Snapshot extends Command {

// start processing snapshots
await this.percy.start();

for (let snapshot of snapshots) {
this.percy.snapshot(snapshot);
}
this.percy.snapshot(snapshots);
}

// Called on error, interupt, or after running
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/percy.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,11 @@ export default class Percy {
throw new Error('Not running');
}

// handle multiple snapshots
if (Array.isArray(options)) {
return Promise.all(options.map(o => this.snapshot(o)));
}

// get derived snapshot config options
let snapshot = getSnapshotConfig(this, options);

Expand Down
9 changes: 7 additions & 2 deletions packages/core/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export function createServer(routes) {
// create a simple server to route request responses
context.routes = routes;
context.server = http.createServer((request, response) => {
request.params = new URLSearchParams(request.url.split('?')[1]);

request.on('data', chunk => {
request.body = (request.body || '') + chunk;
});
Expand Down Expand Up @@ -126,8 +128,11 @@ export default function createPercyServer(percy) {
}),

// forward snapshot requests
'/percy/snapshot': ({ body }) => percy.snapshot(body)
.then(() => [200, 'application/json', { success: true }]),
'/percy/snapshot': async ({ body, params }) => {
let snapshot = percy.snapshot(body);
if (!params.has('async')) await snapshot;
return [200, 'application/json', { success: true }];
},

// stops the instance async at the end of the event loop
'/percy/stop': () => setImmediate(() => percy.stop()) && (
Expand Down
15 changes: 15 additions & 0 deletions packages/core/test/server.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ describe('Server', () => {
expect(percy.snapshot).toHaveBeenCalledOnceWith({ 'test-me': true, me_too: true });
});

it('can handle snapshots async with a parameter', async () => {
let test = new Promise(r => setTimeout(r, 500));
spyOn(percy, 'snapshot').and.returnValue(test);
await percy.start();

let response = await fetch('http://localhost:1337/percy/snapshot?async', {
method: 'post',
body: '{}'
});

await expectAsync(response.json()).toBeResolvedTo({ success: true });
await expectAsync(test).toBePending();
await test; // no hanging promises
});

it('returns a 500 error when an endpoint throws', async () => {
spyOn(percy, 'snapshot').and.rejectWith(new Error('test error'));
await percy.start();
Expand Down
16 changes: 16 additions & 0 deletions packages/core/test/snapshot.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,22 @@ describe('Snapshot', () => {
]);
});

it('accepts multiple snapshots', async () => {
await percy.snapshot([{
url: 'http://localhost:8000/one',
domSnapshot: testDOM
}, {
url: 'http://localhost:8000/two',
domSnapshot: testDOM
}]);

expect(logger.stderr).toEqual([]);
expect(logger.stdout).toEqual([
'[percy] Snapshot taken: /one',
'[percy] Snapshot taken: /two'
]);
});

it('handles the browser closing early', async () => {
spyOn(percy.browser, 'page').and.callThrough();

Expand Down
9 changes: 4 additions & 5 deletions packages/sdk-utils/src/post-snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ import request from './request';

// Post snapshot data to the snapshot endpoint. If the snapshot endpoint responds with a closed
// error message, signal that Percy has been disabled.
export default async function postSnapshot(options) {
await request('/percy/snapshot', {
method: 'POST',
body: JSON.stringify(options)
}).catch(err => {
export default async function postSnapshot(options, params) {
let query = params ? `?${new URLSearchParams(params)}` : '';

await request.post(`/percy/snapshot${query}`, options).catch(err => {
if (err.response && err.message === 'Closed') {
percy.enabled = false;
} else {
Expand Down
7 changes: 7 additions & 0 deletions packages/sdk-utils/src/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ export default async function request(path, options = {}) {
return response;
}

request.post = function post(url, json) {
return request(url, {
method: 'POST',
body: JSON.stringify(json)
});
};

// environment specific implementation
if (process.env.__PERCY_BROWSERIFIED__) {
// use window.fetch in browsers
Expand Down
10 changes: 9 additions & 1 deletion packages/sdk-utils/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ describe('SDK Utils', () => {
});
});

describe('postSnapshot(options)', () => {
describe('postSnapshot(options[, params])', () => {
let { postSnapshot } = utils;
let options;

Expand Down Expand Up @@ -167,5 +167,13 @@ describe('SDK Utils', () => {
await expectAsync(postSnapshot({})).toBeResolved();
expect(utils.percy.enabled).toEqual(false);
});

it('accepts URL parameters as the second argument', async () => {
let params = { test: 'foobar' };
let expected = `/percy/snapshot?${new URLSearchParams(params)}`;

await expectAsync(postSnapshot(options, params)).toBeResolved();
await expectAsync(helpers.getRequests()).toBeResolvedTo([[expected, options]]);
});
});
});

0 comments on commit c6ff602

Please sign in to comment.