@percy/core 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/utils.js CHANGED
@@ -1,12 +1,14 @@
1
1
  import EventEmitter from 'events';
2
2
  import { sha256hash } from '@percy/client/utils';
3
3
  export { request, getPackageJSON, hostnameMatches } from '@percy/client/utils';
4
- export { Server, createServer } from './server.js'; // Returns the hostname portion of a URL.
4
+ export { Server, createServer } from './server.js';
5
5
 
6
+ // Returns the hostname portion of a URL.
6
7
  export function hostname(url) {
7
8
  return new URL(url).hostname;
8
- } // Normalizes a URL by stripping hashes to ensure unique resources.
9
+ }
9
10
 
11
+ // Normalizes a URL by stripping hashes to ensure unique resources.
10
12
  export function normalizeURL(url) {
11
13
  let {
12
14
  protocol,
@@ -15,25 +17,29 @@ export function normalizeURL(url) {
15
17
  search
16
18
  } = new URL(url);
17
19
  return `${protocol}//${host}${pathname}${search}`;
18
- } // Creates a local resource object containing the resource URL, mimetype, content, sha, and any
19
- // other additional resources attributes.
20
+ }
20
21
 
22
+ // Creates a local resource object containing the resource URL, mimetype, content, sha, and any
23
+ // other additional resources attributes.
21
24
  export function createResource(url, content, mimetype, attrs) {
22
- return { ...attrs,
25
+ return {
26
+ ...attrs,
23
27
  sha: sha256hash(content),
24
28
  mimetype,
25
29
  content,
26
30
  url
27
31
  };
28
- } // Creates a root resource object with an additional `root: true` property. The URL is normalized
29
- // here as a convenience since root resources are usually created outside of asset discovery.
32
+ }
30
33
 
34
+ // Creates a root resource object with an additional `root: true` property. The URL is normalized
35
+ // here as a convenience since root resources are usually created outside of asset discovery.
31
36
  export function createRootResource(url, content) {
32
37
  return createResource(normalizeURL(url), content, 'text/html', {
33
38
  root: true
34
39
  });
35
- } // Creates a Percy CSS resource object.
40
+ }
36
41
 
42
+ // Creates a Percy CSS resource object.
37
43
  export function createPercyCSSResource(url, css) {
38
44
  let {
39
45
  href,
@@ -42,21 +48,24 @@ export function createPercyCSSResource(url, css) {
42
48
  return createResource(href, css, 'text/css', {
43
49
  pathname
44
50
  });
45
- } // Creates a log resource object.
51
+ }
46
52
 
53
+ // Creates a log resource object.
47
54
  export function createLogResource(logs) {
48
55
  let [url, content] = [`/percy.${Date.now()}.log`, JSON.stringify(logs)];
49
56
  return createResource(url, content, 'text/plain', {
50
57
  log: true
51
58
  });
52
- } // Returns true or false if the provided object is a generator or not
59
+ }
53
60
 
61
+ // Returns true or false if the provided object is a generator or not
54
62
  export function isGenerator(subject) {
55
63
  return typeof (subject === null || subject === void 0 ? void 0 : subject.next) === 'function' && (typeof subject[Symbol.iterator] === 'function' || typeof subject[Symbol.asyncIterator] === 'function');
56
- } // Iterates over the provided generator and resolves to the final value when done. With an
64
+ }
65
+
66
+ // Iterates over the provided generator and resolves to the final value when done. With an
57
67
  // AbortSignal, the generator will throw with the abort reason when aborted. Also accepts an
58
68
  // optional node-style callback, called before the returned promise resolves.
59
-
60
69
  export async function generatePromise(gen, signal, cb) {
61
70
  try {
62
71
  if (typeof signal === 'function') [cb, signal] = [signal];
@@ -68,27 +77,24 @@ export async function generatePromise(gen, signal, cb) {
68
77
  done: true,
69
78
  value: await gen
70
79
  } : await gen.next();
71
-
72
80
  while (!done) {
73
81
  var _signal;
74
-
75
82
  ({
76
83
  done,
77
84
  value
78
85
  } = (_signal = signal) !== null && _signal !== void 0 && _signal.aborted ? await gen.throw(signal.reason) : await gen.next(value));
79
86
  }
80
-
81
87
  if (!cb) return value;
82
88
  return cb(null, value);
83
89
  } catch (error) {
84
90
  if (!cb) throw error;
85
91
  return cb(error);
86
92
  }
87
- } // Bare minimum AbortController polyfill for Node < 16.14
93
+ }
88
94
 
95
+ // Bare minimum AbortController polyfill for Node < 16.14
89
96
  export class AbortController {
90
97
  signal = new EventEmitter();
91
-
92
98
  abort(reason = new AbortError()) {
93
99
  if (this.signal.aborted) return;
94
100
  Object.assign(this.signal, {
@@ -97,9 +103,9 @@ export class AbortController {
97
103
  });
98
104
  this.signal.emit('abort', reason);
99
105
  }
106
+ }
100
107
 
101
- } // Similar to DOMException[AbortError] but accepts additional properties
102
-
108
+ // Similar to DOMException[AbortError] but accepts additional properties
103
109
  export class AbortError extends Error {
104
110
  constructor(msg = 'This operation was aborted', props) {
105
111
  Object.assign(super(msg), {
@@ -107,44 +113,44 @@ export class AbortError extends Error {
107
113
  ...props
108
114
  });
109
115
  }
116
+ }
110
117
 
111
- } // An async generator that yields after every event loop until the promise settles
112
-
118
+ // An async generator that yields after every event loop until the promise settles
113
119
  export async function* yieldTo(subject) {
114
120
  // yield to any provided generator or return non-promise values
115
121
  if (isGenerator(subject)) return yield* subject;
116
- if (typeof (subject === null || subject === void 0 ? void 0 : subject.then) !== 'function') return subject; // update local variables with the provided promise
122
+ if (typeof (subject === null || subject === void 0 ? void 0 : subject.then) !== 'function') return subject;
117
123
 
124
+ // update local variables with the provided promise
118
125
  let result,
119
- error,
120
- pending = !!subject.then(r => result = r, e => error = e).finally(() => pending = false);
121
- /* eslint-disable-next-line no-unmodified-loop-condition */
126
+ error,
127
+ pending = !!subject.then(r => result = r, e => error = e).finally(() => pending = false);
122
128
 
129
+ /* eslint-disable-next-line no-unmodified-loop-condition */
123
130
  while (pending) yield new Promise(r => setImmediate(r));
124
-
125
131
  if (error) throw error;
126
132
  return result;
127
- } // An async generator that runs provided generators concurrently
133
+ }
128
134
 
135
+ // An async generator that runs provided generators concurrently
129
136
  export async function* yieldAll(all) {
130
137
  let res = new Array(all.length).fill();
131
138
  all = all.map(yieldTo);
132
-
133
139
  while (true) {
134
140
  res = await Promise.all(all.map((g, i) => {
135
141
  var _res$i, _res$i2;
136
-
137
142
  return (_res$i = res[i]) !== null && _res$i !== void 0 && _res$i.done ? res[i] : g.next((_res$i2 = res[i]) === null || _res$i2 === void 0 ? void 0 : _res$i2.value);
138
143
  }));
139
144
  let vals = res.map(r => r === null || r === void 0 ? void 0 : r.value);
140
145
  if (res.some(r => !(r !== null && r !== void 0 && r.done))) yield vals;else return vals;
141
146
  }
142
- } // An async generator that infinitely yields to the predicate function until a truthy value is
147
+ }
148
+
149
+ // An async generator that infinitely yields to the predicate function until a truthy value is
143
150
  // returned. When a timeout is provided, an error will be thrown during the next iteration after the
144
151
  // timeout has been exceeded. If an idle option is provided, the predicate will be yielded to a
145
152
  // second time, after the idle period, to ensure the yielded value is still truthy. The poll option
146
153
  // determines how long to wait before yielding to the predicate function during each iteration.
147
-
148
154
  export async function* yieldFor(predicate, options = {}) {
149
155
  if (Number.isInteger(options)) options = {
150
156
  timeout: options
@@ -156,7 +162,6 @@ export async function* yieldFor(predicate, options = {}) {
156
162
  } = options;
157
163
  let start = Date.now();
158
164
  let done, value;
159
-
160
165
  while (true) {
161
166
  if (timeout && Date.now() - start >= timeout) {
162
167
  throw new Error(`Timeout of ${timeout}ms exceeded.`);
@@ -168,88 +173,85 @@ export async function* yieldFor(predicate, options = {}) {
168
173
  return value;
169
174
  }
170
175
  }
171
- } // Promisified version of `yieldFor` above.
176
+ }
172
177
 
178
+ // Promisified version of `yieldFor` above.
173
179
  export function waitFor() {
174
180
  return generatePromise(yieldFor(...arguments));
175
- } // Promisified version of `setTimeout` (no callback argument).
181
+ }
176
182
 
183
+ // Promisified version of `setTimeout` (no callback argument).
177
184
  export function waitForTimeout() {
178
185
  return new Promise(resolve => setTimeout(resolve, ...arguments));
179
- } // Browser-specific util to wait for a query selector to exist within an optional timeout.
186
+ }
180
187
 
188
+ // Browser-specific util to wait for a query selector to exist within an optional timeout.
181
189
  /* istanbul ignore next: tested, but coverage is stripped */
182
-
183
190
  async function waitForSelector(selector, timeout) {
184
191
  try {
185
192
  return await waitFor(() => document.querySelector(selector), timeout);
186
193
  } catch {
187
194
  throw new Error(`Unable to find: ${selector}`);
188
195
  }
189
- } // Browser-specific util to wait for an xpath selector to exist within an optional timeout.
196
+ }
190
197
 
198
+ // Browser-specific util to wait for an xpath selector to exist within an optional timeout.
191
199
  /* istanbul ignore next: tested, but coverage is stripped */
192
-
193
-
194
200
  async function waitForXPath(selector, timeout) {
195
201
  try {
196
202
  let xpath = () => document.evaluate(selector, document, null, 9, null);
197
-
198
203
  return await waitFor(() => xpath().singleNodeValue, timeout);
199
204
  } catch {
200
205
  throw new Error(`Unable to find: ${selector}`);
201
206
  }
202
- } // Browser-specific util to scroll to the bottom of a page, optionally calling the provided function
203
- // after each window segment has been scrolled.
207
+ }
204
208
 
209
+ // Browser-specific util to scroll to the bottom of a page, optionally calling the provided function
210
+ // after each window segment has been scrolled.
205
211
  /* istanbul ignore next: tested, but coverage is stripped */
206
-
207
-
208
212
  async function scrollToBottom(options, onScroll) {
209
213
  if (typeof options === 'function') [onScroll, options] = [options];
210
-
211
214
  let size = () => Math.ceil(document.body.scrollHeight / window.innerHeight);
212
-
213
215
  for (let s, i = 1; i < (s = size()); i++) {
214
216
  var _onScroll;
215
-
216
- window.scrollTo({ ...options,
217
+ window.scrollTo({
218
+ ...options,
217
219
  top: window.innerHeight * i
218
220
  });
219
221
  await ((_onScroll = onScroll) === null || _onScroll === void 0 ? void 0 : _onScroll(i, s));
220
222
  }
221
- } // Used to test if a string looks like a function
222
-
223
+ }
223
224
 
224
- const FUNC_REG = /^(async\s+)?(function\s*)?(\w+\s*)?\(.*?\)\s*(\{|=>)/is; // Serializes the provided function with percy helpers for use in evaluating browser scripts
225
+ // Used to test if a string looks like a function
226
+ const FUNC_REG = /^(async\s+)?(function\s*)?(\w+\s*)?\(.*?\)\s*(\{|=>)/is;
225
227
 
228
+ // Serializes the provided function with percy helpers for use in evaluating browser scripts
226
229
  export function serializeFunction(fn) {
227
230
  // stringify or convert a function body into a complete function
228
- let fnbody = typeof fn === 'string' && !FUNC_REG.test(fn) ? `async function eval() {\n${fn}\n}` : fn.toString(); // we might have a function shorthand if this fails
231
+ let fnbody = typeof fn === 'string' && !FUNC_REG.test(fn) ? `async function eval() {\n${fn}\n}` : fn.toString();
229
232
 
233
+ // we might have a function shorthand if this fails
230
234
  /* eslint-disable-next-line no-new, no-new-func */
231
-
232
235
  try {
233
236
  new Function(`(${fnbody})`);
234
237
  } catch (error) {
235
238
  fnbody = fnbody.startsWith('async ') ? fnbody.replace(/^async/, 'async function') : `function ${fnbody}`;
236
- /* eslint-disable-next-line no-new, no-new-func */
237
239
 
240
+ /* eslint-disable-next-line no-new, no-new-func */
238
241
  try {
239
242
  new Function(`(${fnbody})`);
240
243
  } catch (error) {
241
244
  throw new Error('The provided function is not serializable');
242
245
  }
243
- } // wrap the function body with percy helpers
244
-
246
+ }
245
247
 
248
+ // wrap the function body with percy helpers
246
249
  fnbody = 'function withPercyHelpers() {\n' + ['const { config, snapshot } = window.__PERCY__ ?? {};', `return (${fnbody})({`, ' config, snapshot, generatePromise, yieldFor,', ' waitFor, waitForTimeout, waitForSelector, waitForXPath,', ' scrollToBottom', '}, ...arguments);', `${isGenerator}`, `${generatePromise}`, `${yieldFor}`, `${waitFor}`, `${waitForTimeout}`, `${waitForSelector}`, `${waitForXPath}`, `${scrollToBottom}`].join('\n') + '\n}';
247
- /* istanbul ignore else: ironic. */
248
250
 
251
+ /* istanbul ignore else: ironic. */
249
252
  if (fnbody.includes('cov_')) {
250
253
  // remove coverage statements during testing
251
254
  fnbody = fnbody.replace(/cov_.*?(;\n?|,)\s*/g, '');
252
255
  }
253
-
254
256
  return fnbody;
255
257
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percy/core",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -39,10 +39,10 @@
39
39
  "test:types": "tsd"
40
40
  },
41
41
  "dependencies": {
42
- "@percy/client": "1.12.0",
43
- "@percy/config": "1.12.0",
44
- "@percy/dom": "1.12.0",
45
- "@percy/logger": "1.12.0",
42
+ "@percy/client": "1.13.0",
43
+ "@percy/config": "1.13.0",
44
+ "@percy/dom": "1.13.0",
45
+ "@percy/logger": "1.13.0",
46
46
  "content-disposition": "^0.5.4",
47
47
  "cross-spawn": "^7.0.3",
48
48
  "extract-zip": "^2.0.1",
@@ -53,5 +53,5 @@
53
53
  "rimraf": "^3.0.2",
54
54
  "ws": "^8.0.0"
55
55
  },
56
- "gitHead": "4303b74df91f60e36065141289d2ef2277d1d6fc"
56
+ "gitHead": "d2e812d14aa446fa580ffa75144a6280627b5a27"
57
57
  }