html-snapshots 0.19.0 → 1.0.1

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.
Files changed (47) hide show
  1. package/.github/workflows/verify.yml +2 -2
  2. package/HISTORY.md +42 -0
  3. package/README.md +92 -244
  4. package/examples/README.md +149 -0
  5. package/examples/custom/package-lock.json +1090 -9
  6. package/examples/custom/package.json +1 -1
  7. package/examples/debug-phantomjs/package-lock.json +1977 -292
  8. package/examples/debug-phantomjs/package.json +1 -1
  9. package/examples/debug-phantomjs/snapshot.js +1 -0
  10. package/examples/debug-puppeteer/README.md +18 -0
  11. package/examples/debug-puppeteer/package-lock.json +2804 -0
  12. package/examples/debug-puppeteer/package.json +12 -0
  13. package/examples/debug-puppeteer/snapshot.js +30 -0
  14. package/examples/html5rocks/package-lock.json +734 -18
  15. package/examples/html5rocks/package.json +1 -1
  16. package/examples/html5rocks/snapshot.js +0 -6
  17. package/examples/process-limit/package-lock.json +734 -18
  18. package/examples/process-limit/package.json +1 -1
  19. package/examples/simple-promise/package-lock.json +734 -18
  20. package/examples/simple-promise/package.json +1 -1
  21. package/examples/sitemap-index/package-lock.json +734 -18
  22. package/examples/sitemap-index/package.json +1 -1
  23. package/examples/sitemap-index/snapshot.js +0 -1
  24. package/examples/verbose/package-lock.json +734 -19
  25. package/examples/verbose/package.json +1 -1
  26. package/examples/verbose/snapshot.js +1 -0
  27. package/lib/html-snapshots.js +87 -4
  28. package/lib/input-generators/_base.js +7 -1
  29. package/lib/puppeteer/index.js +98 -0
  30. package/lib/puppeteer/removeScripts.js +8 -0
  31. package/package.json +6 -2
  32. package/test/mocha/browsers/puppeteer.js +106 -0
  33. package/test/mocha/browsers/server/index.html +9 -0
  34. package/test/mocha/browsers/test.js +11 -0
  35. package/test/mocha/html-snapshots/basics.js +60 -47
  36. package/test/mocha/html-snapshots/phantomjs-options.js +54 -60
  37. package/test/mocha/html-snapshots/process-limit.js +100 -92
  38. package/test/mocha/html-snapshots/puppeteer.js +47 -0
  39. package/test/mocha/html-snapshots/robots.js +137 -120
  40. package/test/mocha/html-snapshots/server/public/page-sitemap.xml +16 -0
  41. package/test/mocha/html-snapshots/sitemap-index.js +89 -80
  42. package/test/mocha/html-snapshots/sitemap.js +75 -31
  43. package/test/mocha/html-snapshots/snapshot-scripts.js +124 -120
  44. package/test/mocha/html-snapshots/test.js +11 -2
  45. package/test/mocha/html-snapshots/use-jquery.js +65 -64
  46. package/test/mocha/html-snapshots/utils.js +29 -29
  47. package/examples/utils/index.js +0 -25
@@ -7,6 +7,6 @@
7
7
  "start": "node ./snapshot.js"
8
8
  },
9
9
  "dependencies": {
10
- "html-snapshots": "^0.18.3"
10
+ "html-snapshots": "^1.0.0"
11
11
  }
12
12
  }
@@ -23,6 +23,7 @@ htmlSnapshots.run({
23
23
  outputDirClean: true,
24
24
  selector: "#mainContent",
25
25
  timeout: 10000,
26
+ browser: "phantomjs",
26
27
  processLimit: 1, // to clarify output
27
28
  verbose: verboseOption
28
29
  })
@@ -20,8 +20,6 @@ const _ = require("lodash");
20
20
  const common = require("./common");
21
21
  const inputFactory = require("./input-generators");
22
22
  const Notifier = require("./async").Notifier;
23
- const phantomDir = "./phantom";
24
- const snapshotScript = path.join(phantomDir, "default.js");
25
23
 
26
24
  /**
27
25
  * Determine the default phantomJS module path. This is overridden by the
@@ -56,13 +54,21 @@ function defaultPhantomJSExePath () {
56
54
  return phantomSource;
57
55
  }
58
56
 
57
+ const phantomDir = "./phantom";
58
+ const phantomSnapshotScript = path.join(phantomDir, "default.js");
59
+ const puppeteerDir = "./puppeteer";
60
+ const puppeteerSnapshotScript = path.join(puppeteerDir, "index.js");
61
+
59
62
  /**
60
63
  * This module's defaults
61
64
  */
62
65
  const defaults = Object.freeze({
63
66
  input: "robots",
67
+ browser: "puppeteer",
64
68
  phantomjs: defaultPhantomJSExePath(),
65
- snapshotScript: path.join(__dirname, snapshotScript),
69
+ phantomSnapshotScript: path.join(__dirname, phantomSnapshotScript),
70
+ puppeteer: path.join(__dirname, puppeteerSnapshotScript),
71
+ snapshotScript: path.join(__dirname, puppeteerSnapshotScript),
66
72
  outputDirClean: false,
67
73
  pollInterval: 500,
68
74
  processLimit: 4
@@ -92,7 +98,7 @@ const defaults = Object.freeze({
92
98
  * @param {Function} notifier.known - Check if a file is or has been processed.
93
99
  * @param {Function} qcb - async.queue callback function.
94
100
  */
95
- function worker (input, options, notifier, qcb) {
101
+ function phantomjsWorker (input, options, notifier, qcb) {
96
102
  let cp, customModule;
97
103
  let snapshotScript = options.snapshotScript;
98
104
  const phantomjsOptions =
@@ -142,6 +148,73 @@ function worker (input, options, notifier, qcb) {
142
148
  }
143
149
  }
144
150
 
151
+ /**
152
+ * @param {Object} input - An input object from an input generator.
153
+ * @param {String} input.outputFile - The output file that the html is written to.
154
+ * @param {String} input.url - The url to get the html from.
155
+ * @param {String} input.selector - The selector to wait for.
156
+ * @param {String} input.timeout - The entire process timeout.
157
+ * @param {String} input.debug - The debug object.
158
+ * @param {Object} options - Input options.
159
+ * @param {Object} options.snapshotScript - Snapshot script options.
160
+ * @param {String} options.snapshotScript.script - Snapshot script name.
161
+ * @param {String} options.snapshotScript.module - Snapshot script module.
162
+ * @param {String} options.puppeteer - Full path to the puppeteer exec.
163
+ * @param {Object} notifier - The async Notifier instance for this run.
164
+ * @param {Function} notifier.remove - Remove an outputFile from notifier.
165
+ * @param {Function} notifier.setError - Set an error on the notifier.
166
+ * @param {Function} notifier.add - Add a file, timeout to the notifier.
167
+ * @param {Function} notifier.known - Check if a file is or has been processed.
168
+ * @param {Function} qcb - async.queue callback function.
169
+ */
170
+ function puppeteerWorker (input, options, notifier, qcb) {
171
+ let filter = "false";
172
+ let snapshotScript = options.snapshotScript;
173
+
174
+ // If the outputFile has NOT already been seen by the notifier, process.
175
+ if (!notifier.known(input.outputFile)) {
176
+
177
+ // map snapshotScript object script
178
+ if (_.isObject(options.snapshotScript)) {
179
+ snapshotScript = options.puppeteer;
180
+ if (options.snapshotScript.script === "removeScripts") {
181
+ filter = "./removeScripts";
182
+ } else {
183
+ filter = options.snapshotScript.module;
184
+ }
185
+ }
186
+
187
+ const cp = spawn(
188
+ snapshotScript,
189
+ [ input.outputFile,
190
+ input.url,
191
+ input.selector,
192
+ input.timeout,
193
+ filter,
194
+ input.debug.flag,
195
+ input.debug.slowMo
196
+ ], { cwd: process.cwd(), stdio: "inherit", detached: true }
197
+ );
198
+
199
+ cp.on("error", function (e) {
200
+ notifier.remove(input.outputFile);
201
+ notifier.setError(e);
202
+ console.error(e);
203
+ qcb(e);
204
+ });
205
+
206
+ cp.on("exit", function (code) {
207
+ qcb(code);
208
+ });
209
+
210
+ // start counting
211
+ notifier.add(input.outputFile, input.timeout);
212
+ } else {
213
+ // The input.outputFile is being or has been processed this run.
214
+ qcb(0);
215
+ }
216
+ }
217
+
145
218
  /**
146
219
  * Prepare html snapshots options.
147
220
  *
@@ -156,6 +229,13 @@ function prepOptions (options) {
156
229
  if (Array.isArray(options.source)) {
157
230
  options.input = "array";
158
231
  }
232
+
233
+ // If phantomjs browser, make sure the default snapshotScript is correctly set.
234
+ if (options.browser === 'phantomjs') {
235
+ if (!options.snapshotScript || options.snapshotScript === options.puppeteer) {
236
+ options.snapshotScript = options.phantomSnapshotScript;
237
+ }
238
+ }
159
239
  }
160
240
 
161
241
  module.exports = {
@@ -226,6 +306,9 @@ module.exports = {
226
306
  // expose abort callback to input generators via options.
227
307
  options._abort = notifier.abort.bind(notifier, q);
228
308
 
309
+ const worker =
310
+ options.browser === 'puppeteer' ? puppeteerWorker : phantomjsWorker;
311
+
229
312
  // generate input for the snapshots.
230
313
  return inputGenerator.run(options, input => {
231
314
  q.push(_.partial(worker, input, options, notifier));
@@ -26,7 +26,11 @@ const defaults = Object.freeze({
26
26
  checkInterval: 250,
27
27
  useJQuery: false,
28
28
  verbose: false,
29
- phantomjsOptions: ""
29
+ phantomjsOptions: "",
30
+ debug: {
31
+ flag: "false",
32
+ slowMo: 500
33
+ }
30
34
  });
31
35
 
32
36
  /**
@@ -224,6 +228,7 @@ module.exports = {
224
228
  * @param {Function} options.userJQuery - Produces a jquery bool given a page url.
225
229
  * @param {Function} options.verbose - Produces a verbose bool given a page url.
226
230
  * @param {Function} options.phantomjsOptions - Produces phantomjs options given a page url.
231
+ * @param {Function} options.debug - Produces debug options given a page url.
227
232
  * @param {Function} options._inputEmitter - The EventEmitter to generate input.
228
233
  * @param {String} page - The url to the page resource.
229
234
  * @returns {String} The full output file path.
@@ -258,6 +263,7 @@ module.exports = {
258
263
  // map the input page to phantomJS options
259
264
  phantomjsOptions: options.phantomjsOptions(page),
260
265
  // useful for testing, debugging
266
+ debug: options.debug,
261
267
  __page: page
262
268
  });
263
269
 
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env -S node --unhandled-rejections=strict
2
+ /**
3
+ * puppeteer process entry point
4
+ *
5
+ * Command line arguments:
6
+ * 0 - /path/to/node
7
+ * 1 - *this script*
8
+ * 2 - outputFile
9
+ * 3 - url
10
+ * 4 - selector
11
+ * [5] - timeout
12
+ * [6] - filter
13
+ * [7] - debug
14
+ * [8] - slowMo
15
+ */
16
+ const puppeteer = require('puppeteer');
17
+ const path = require('path');
18
+ const { promises: fs } = require('fs');
19
+
20
+ if (process.argv.length < 5) {
21
+ throw Error(`puppeteer script '${
22
+ process.argv[1]
23
+ }' expected these arguments:\n\
24
+ OUTPUTFILE URL SELECTOR [TIMEOUT FILTER DEBUG SLOWMO]`);
25
+ }
26
+
27
+ const options = {
28
+ outputFile: process.argv[2],
29
+ url: process.argv[3],
30
+ selector: process.argv[4],
31
+ timeout: process.argv[5] ? parseInt(process.argv[5], 10) : 10000,
32
+ filter: process.argv[6] && process.argv[6] !== 'false' ? process.argv[6] : null,
33
+ debug: process.argv[7] && process.argv[7].toLowerCase() === 'true' ? true : false,
34
+ slowMo: process.argv[8] ? parseInt(process.argv[8], 10) : 500
35
+ };
36
+ const launchOptions = options.debug ? {
37
+ headless: false,
38
+ slowMo: options.slowMo,
39
+ devtools: true
40
+ } : {};
41
+
42
+ /**
43
+ * Run the snapshot script.
44
+ *
45
+ * @returns {Promise} on completion
46
+ */
47
+ async function run () {
48
+ let timeLeft, browser;
49
+ const start = Date.now();
50
+
51
+ timeLeft = options.timeout - 200; // leave some room for reporting
52
+ if (timeLeft < 0) timeLeft = 0;
53
+
54
+ try {
55
+ const filter =
56
+ options.filter ? require(options.filter) : passthru => passthru;
57
+
58
+ browser = await puppeteer.launch(launchOptions);
59
+ const page = await browser.newPage();
60
+ await page.goto(options.url, {
61
+ timeout: timeLeft
62
+ });
63
+
64
+ timeLeft -= (Date.now() - start);
65
+ if (timeLeft < 0) timeLeft = 0;
66
+
67
+ await fs.mkdir(path.dirname(options.outputFile), {
68
+ recursive: true
69
+ });
70
+
71
+ await page.waitForSelector(options.selector, {
72
+ Visible: true,
73
+ timeout: timeLeft
74
+ });
75
+
76
+ const content = await page.content();
77
+ await fs.writeFile(options.outputFile, filter(content));
78
+ const elapsed = Date.now() - start;
79
+
80
+ await browser.close();
81
+
82
+ return `snapshot for ${options.url} finished in ${elapsed} ms\n\
83
+ written to ${options.outputFile}`;
84
+ } catch (e) {
85
+ const err = new Error(`failed to snapshot ${options.url}`, {
86
+ cause: e
87
+ });
88
+ if (browser) {
89
+ browser.close().finally(() => {
90
+ throw err;
91
+ });
92
+ }
93
+ throw err;
94
+ }
95
+ }
96
+ run().then(console.log).catch(e => {
97
+ throw e;
98
+ });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Remove all script tags from content
3
+ */
4
+ function removeScriptTags (content) {
5
+ return content.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
6
+ }
7
+
8
+ module.exports = removeScriptTags;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "html-snapshots",
3
- "version": "0.19.0",
3
+ "version": "1.0.1",
4
4
  "author": {
5
5
  "name": "Alex Grant",
6
6
  "email": "alex@localnerve.com",
@@ -22,7 +22,10 @@
22
22
  "test": "mocha test/mocha/**/*.js --exit --recursive --reporter spec",
23
23
  "test:async": "mocha test/mocha/async/test.js --exit --reporter spec",
24
24
  "test:common": "mocha test/mocha/common/*.js --exit --reporter spec",
25
+ "test:browsers": "mocha test/mocha/browsers/test.js --exit --reporter spec",
26
+ "test:browsers:cover": "c8 -- npm run test:browsers",
25
27
  "test:input-generators": "mocha test/mocha/input-generators/*.js --exit --reporter spec",
28
+ "test:input-generators:debug": "mocha --inspect-brk test/mocha/input-generators/*.js --exit --reporter spec",
26
29
  "test:html-snapshots": "mocha test/mocha/html-snapshots/*.js --exit --reporter spec",
27
30
  "test:html-snapshots:debug": "mocha --inspect-brk test/mocha/html-snapshots/*.js --exit --reporter spec",
28
31
  "test:cover": "c8 -- npm test",
@@ -50,10 +53,11 @@
50
53
  "async": "3.2.4",
51
54
  "async-lock": "1.3.2",
52
55
  "combine-errors": "3.0.3",
56
+ "got": "12.5.1",
53
57
  "lodash": "4.17.21",
54
58
  "mkdirp": "1.0.4",
55
59
  "phantomjs-prebuilt": "2.1.16",
56
- "got": "12.5.0",
60
+ "puppeteer": "^18.0.5",
57
61
  "rimraf": "3.0.2",
58
62
  "xml2js": "0.4.23"
59
63
  },
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Tests that target the puppeteer script specifically.
3
+ *
4
+ * Copyright (c) 2013 - 2022, Alex Grant, LocalNerve, contributors
5
+ */
6
+ /* global before, it */
7
+ const assert = require("assert");
8
+ const path = require("path");
9
+ const rimraf = require("rimraf").sync;
10
+ const spawn = require("child_process").spawn;
11
+ const server = require("../../server");
12
+
13
+ const outputDir = path.join(__dirname, "tmp");
14
+ const port = 9340;
15
+
16
+ function puppeteerTests () {
17
+ const snapshotScript = path.resolve(__dirname, '../../../lib/puppeteer/index.js');
18
+ const outputFile = path.join(outputDir, "snapshot.html");
19
+
20
+ /**
21
+ * Launch the puppeteer script as a process.
22
+ *
23
+ * @param {Array} args - array of puppeteer args
24
+ */
25
+ function spawnPuppeteer (args, done) {
26
+ const cp = spawn(
27
+ snapshotScript,
28
+ args
29
+ , { cwd: process.cwd(), stdio: "inherit", detached: true }
30
+ );
31
+
32
+ cp.on("exit", code => {
33
+ if (code !== 0) {
34
+ return done(new Error(`error exit code: ${code}`));
35
+ }
36
+ done();
37
+ });
38
+ }
39
+
40
+ return function () {
41
+ before(function() {
42
+ server.start(path.join(__dirname, "./server"), port);
43
+ });
44
+
45
+ it("should succeed with minimal args", function (done) {
46
+ this.timeout(5000);
47
+
48
+ rimraf(outputDir);
49
+
50
+ spawnPuppeteer([
51
+ outputFile,
52
+ `http://localhost:${port}/`,
53
+ "body"
54
+ ], done);
55
+ });
56
+
57
+ it("should fail if insufficient input args", function (done) {
58
+ rimraf(outputDir);
59
+
60
+ spawnPuppeteer([
61
+ outputFile,
62
+ "http://someurl.domain/path/resource"
63
+ ], e => {
64
+ const msg = "expected process error";
65
+ assert.ok(e, msg);
66
+ done(e ? undefined : new Error(msg));
67
+ });
68
+ });
69
+
70
+ it("should fail with bad url", function (done) {
71
+ rimraf(outputDir);
72
+
73
+ spawnPuppeteer([
74
+ outputFile,
75
+ "http://nonexistent.domain/path/resource",
76
+ "body",
77
+ 1000
78
+ ], e => {
79
+ const msg = "expected process error";
80
+ assert.ok(e, msg);
81
+ done(e ? undefined : new Error(msg));
82
+ });
83
+ });
84
+
85
+ it("should launch debugging", function (done) {
86
+ const timeout = 5000;
87
+ this.timeout(timeout);
88
+
89
+ rimraf(outputDir);
90
+
91
+ spawnPuppeteer([
92
+ outputFile,
93
+ `http://localhost:${port}/`,
94
+ "body",
95
+ timeout - 1000,
96
+ "false",
97
+ "true",
98
+ 10
99
+ ], done);
100
+ });
101
+ };
102
+ }
103
+
104
+ module.exports = {
105
+ testSuite: puppeteerTests
106
+ };
@@ -0,0 +1,9 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en-US">
3
+ <body>
4
+ <h1>Hello World</h1>
5
+ <p>
6
+ This is a simple test file, how do you do?
7
+ </p>
8
+ </body>
9
+ </html>
@@ -0,0 +1,11 @@
1
+ /**
2
+ * browser process specific tests
3
+ *
4
+ * Copyright (c) 2013 - 2022, Alex Grant, LocalNerve, contributors
5
+ */
6
+ /* global describe */
7
+ const puppeteer = require("./puppeteer");
8
+
9
+ describe("browser process tests", function () {
10
+ describe("puppeteer", puppeteer.testSuite());
11
+ });
@@ -4,68 +4,81 @@
4
4
  * Copyright (c) 2013 - 2022, Alex Grant, LocalNerve, contributors
5
5
  */
6
6
  /* global it */
7
- var assert = require("assert");
8
- var _ = require("lodash");
9
- var fs = require("fs");
10
- var path = require("path");
11
- var ss = require("../../../lib/html-snapshots");
12
- var utils = require("./utils");
13
- var optHelp = require("../../helpers/options");
7
+ const assert = require("assert");
8
+ const _ = require("lodash");
9
+ const fs = require("fs");
10
+ const path = require("path");
11
+ const ss = require("../../../lib/html-snapshots");
12
+ const utils = require("./utils");
13
+ const optHelp = require("../../helpers/options");
14
14
 
15
15
  // missing destructuring, will write postcard...
16
- var bogusFile = utils.bogusFile;
17
- var cleanupError = utils.cleanupError;
18
- var unexpectedSuccess = utils.unexpectedSuccess;
16
+ const {
17
+ bogusFile,
18
+ cleanupError,
19
+ unexpectedSuccess
20
+ } = utils;
19
21
 
20
- function basicTests () {
22
+ function basicTests (options) {
23
+ const {
24
+ browsers
25
+ } = options;
21
26
  return function () {
22
- it("no arguments should fail", function (done) {
23
- var twice = _.after(2, cleanupError.bind(null, done, 0));
27
+ browsers.forEach(browser => {
28
+ it(`no arguments should fail - ${browser}`, function (done) {
29
+ const twice = _.after(2, cleanupError.bind(null, done, 0));
24
30
 
25
- ss.run(optHelp.decorate({}), twice)
26
- .then(unexpectedSuccess.bind(null, done))
27
- .catch(twice);
28
- });
31
+ ss.run(optHelp.decorate({
32
+ browser
33
+ }), twice)
34
+ .then(unexpectedSuccess.bind(null, done))
35
+ .catch(twice);
36
+ });
29
37
 
30
- it("invalid source should fail", function (done) {
31
- var twice = _.after(2, cleanupError.bind(null, done, 0));
38
+ it(`invalid source should fail - ${browser}`, function (done) {
39
+ const twice = _.after(2, cleanupError.bind(null, done, 0));
32
40
 
33
- ss.run(optHelp.decorate({ source: bogusFile }), twice)
34
- .then(unexpectedSuccess.bind(null, done))
35
- .catch(twice);
36
- });
41
+ ss.run(optHelp.decorate({
42
+ source: bogusFile,
43
+ browser
44
+ }), twice)
45
+ .then(unexpectedSuccess.bind(null, done))
46
+ .catch(twice);
47
+ });
37
48
 
38
- it("should clean the output directory when specified", function (done) {
39
- var dir = path.join(__dirname, "./tmpdir");
40
- var file = path.join(dir, "somefile.txt");
41
- var twice = _.after(2, cleanupError.bind(null, done, 0));
49
+ it(`should clean the output directory when specified - ${browser}`, function (done) {
50
+ const dir = path.join(__dirname, "./tmpdir");
51
+ const file = path.join(dir, "somefile.txt");
52
+ const twice = _.after(2, cleanupError.bind(null, done, 0));
42
53
 
43
- fs.mkdirSync(dir);
44
- fs.writeFileSync(file, "some data");
45
- assert.doesNotThrow(fs.accessSync.bind(null, dir));
54
+ fs.mkdirSync(dir);
55
+ fs.writeFileSync(file, "some data");
56
+ assert.doesNotThrow(fs.accessSync.bind(null, dir));
46
57
 
47
- ss.run(optHelp.decorate({
48
- source: bogusFile,
49
- outputDir: dir,
50
- outputDirClean: true
51
- }), twice)
52
- .then(unexpectedSuccess.bind(null, done))
53
- .catch(twice);
58
+ ss.run(optHelp.decorate({
59
+ source: bogusFile,
60
+ outputDir: dir,
61
+ outputDirClean: true,
62
+ browser
63
+ }), twice)
64
+ .then(unexpectedSuccess.bind(null, done))
65
+ .catch(twice);
54
66
 
55
- assert.throws(fs.accessSync.bind(null, dir));
56
- });
67
+ assert.throws(fs.accessSync.bind(null, dir));
68
+ });
57
69
 
58
- it("default snapshot script should exist", function (done) {
59
- var options = { source: "./bogus/file.txt" };
60
- var twice = _.after(2, cleanupError.bind(null, done, 0));
70
+ it(`default snapshot script should exist - ${browser}`, function (done) {
71
+ const options = { source: "./bogus/file.txt", browser };
72
+ const twice = _.after(2, cleanupError.bind(null, done, 0));
61
73
 
62
- var result = ss.run(optHelp.decorate(options), twice);
74
+ const result = ss.run(optHelp.decorate(options), twice);
63
75
 
64
- assert.doesNotThrow(fs.accessSync.bind(null, options.snapshotScript));
76
+ assert.doesNotThrow(fs.accessSync.bind(null, options.snapshotScript));
65
77
 
66
- result
67
- .then(unexpectedSuccess.bind(null, done))
68
- .catch(twice);
78
+ result
79
+ .then(unexpectedSuccess.bind(null, done))
80
+ .catch(twice);
81
+ });
69
82
  });
70
83
  };
71
84
  }