1 | const { resolve } = require('path');
|
2 | const erte = require('@artdeco/erte');
|
3 | const { equal } = require('assert');
|
4 | const { confirm } = require('reloquent');
|
5 | const { inspect } = require('util');
|
6 | const read = require('@wrote/read');
|
7 | const write = require('@wrote/write');
|
8 | const ensurePath = require('@wrote/ensure-path');
|
9 | const erotic = require('erotic');
|
10 | const frame = require('frame-of-mind');
|
11 |
|
12 | const isJSON = p => /\.json$/.test(p)
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | class SnapshotContext {
|
18 | constructor() {
|
19 | this.snapshotsDir = 'test/snapshot'
|
20 | }
|
21 | |
22 |
|
23 |
|
24 |
|
25 | setDir(dir) {
|
26 | this.snapshotsDir = dir
|
27 | }
|
28 | async save(path, snapshot) {
|
29 | const p = resolve(this.snapshotsDir, path)
|
30 | await ensurePath(p)
|
31 | if (isJSON(p)) {
|
32 | const data = JSON.stringify(snapshot, null, 2)
|
33 | await write(p, data)
|
34 | } else {
|
35 | await write(p, snapshot)
|
36 | }
|
37 | }
|
38 | async prompt(snapshot, name) {
|
39 | if (typeof snapshot == 'string') {
|
40 | let maxWidth = snapshot.split(/\r?\n/).reduce((acc, current) => {
|
41 | if (current.length > acc) return current.length
|
42 | }, 0)
|
43 | if (process.stdout.isTTY && process.stdout.columns - 4 >= maxWidth) {
|
44 | console.log(frame(snapshot))
|
45 | } else {
|
46 | console.log(snapshot)
|
47 | }
|
48 | } else {
|
49 | console.log(inspect(snapshot, { colors: true }))
|
50 | }
|
51 | const answer = await confirm(`Save snapshot${name ? ` for ${name}` : ''}?`)
|
52 | return answer
|
53 | }
|
54 | async promptAndSave(path, actual, name) {
|
55 | if (!actual) throw new Error('Give snapshot to save')
|
56 | const res = await this.prompt(actual, name)
|
57 | if (res) {
|
58 | await this.save(path, actual)
|
59 | } else {
|
60 | throw new Error('Could not test missing snapshot')
|
61 | }
|
62 | }
|
63 | async read(path) {
|
64 | const p = resolve(this.snapshotsDir, path)
|
65 | if (isJSON(p)) {
|
66 | const data = await read(p)
|
67 | const snapshot = JSON.parse(data)
|
68 | return snapshot
|
69 | } else {
|
70 | const snapshot = await read(p)
|
71 | return snapshot
|
72 | }
|
73 | }
|
74 | |
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 | async test(path, actual, name, interactive = false) {
|
82 | if (!actual) throw new Error('Pass the actual value for snapshot.')
|
83 | const cb = erotic(true)
|
84 | const json = isJSON(path)
|
85 | let expected
|
86 | try {
|
87 | expected = await this.read(path)
|
88 | if (json) {
|
89 | const { deepEqual } = require('@zoroaster/assert')
|
90 | deepEqual(actual, expected)
|
91 | } else {
|
92 | equal(actual, expected)
|
93 | }
|
94 | } catch (err) {
|
95 | if (err.code == 'ENOENT') {
|
96 | await this.promptAndSave(path, actual, name)
|
97 | return
|
98 | }
|
99 | let erteString
|
100 | if (!json) {
|
101 | erteString = erte(expected, actual)
|
102 | }
|
103 | if (interactive) {
|
104 | if (json) console.log(err.message)
|
105 | else console.log(erteString)
|
106 | const upd = await confirm(`Update snapshot${name ? ` for ${name}` : ''}?`)
|
107 | if (upd) {
|
108 | await this.save(path, actual)
|
109 | return
|
110 | }
|
111 | }
|
112 | if (!json) {
|
113 | !interactive && console.log(erteString)
|
114 | const e = cb('The string didn\'t match the snapshot.')
|
115 | e.erte = erteString
|
116 | throw e
|
117 | }
|
118 | const e = cb(err)
|
119 | throw e
|
120 | }
|
121 | }
|
122 | }
|
123 |
|
124 |
|
125 | module.exports = SnapshotContext |
\ | No newline at end of file |