1 | 'use strict'
|
2 |
|
3 | const debug = require('debug')('snap-shot')
|
4 | const stackSites = require('stack-sites')
|
5 | const callsites = require('callsites')
|
6 | const la = require('lazy-ass')
|
7 | const is = require('check-more-types')
|
8 | const getSpecFunction = require('find-test-caller')
|
9 | const {strip} = require('./utils')
|
10 | const snapShotCore = require('snap-shot-core')
|
11 | const compare = require('./compare')
|
12 |
|
13 | const isNode = Boolean(require('fs').existsSync)
|
14 | const isBrowser = !isNode
|
15 | const isCypress = isBrowser && typeof cy === 'object'
|
16 |
|
17 | let fs
|
18 | if (isNode) {
|
19 | fs = require('./file-system')
|
20 | } else if (isCypress) {
|
21 | fs = require('./cypress-system')
|
22 | } else {
|
23 | fs = require('./browser-system')
|
24 | }
|
25 |
|
26 | const opts = {
|
27 | show: Boolean(process.env.SHOW),
|
28 | dryRun: Boolean(process.env.DRY),
|
29 | update: Boolean(process.env.UPDATE),
|
30 | ci: Boolean(process.env.CI)
|
31 | }
|
32 |
|
33 | const SNAP_SHOT_EXTENSION = '.snap-shot'
|
34 |
|
35 | function getSpec ({file, line}) {
|
36 | return getSpecFunction({file, line, fs})
|
37 | }
|
38 |
|
39 | function dataDriven (fn, inputs) {
|
40 | la(is.fn(fn), 'expected a function for data-driven test', fn)
|
41 | la(is.array(inputs),
|
42 | 'expected list of inputs', inputs, 'to function', fn.name)
|
43 | const name = fn.name
|
44 | const behavior = inputs.map(given => {
|
45 | const args = Array.isArray(given) ? given : [given]
|
46 | return {
|
47 | given,
|
48 | expect: fn.apply(null, args)
|
49 | }
|
50 | })
|
51 | return {name, behavior}
|
52 | }
|
53 |
|
54 | function snapshot (what, update) {
|
55 | const sites = stackSites()
|
56 | if (sites.length < 3) {
|
57 |
|
58 | if (this && this.test && this.test.title) {
|
59 | debug('no callsite, but have test title "%s"', this.test.title)
|
60 | return this.test.title
|
61 | }
|
62 | debug(sites)
|
63 | const msg = 'Do not have caller function callsite'
|
64 | throw new Error(msg)
|
65 | }
|
66 | debug('%d callsite(s)', sites.length)
|
67 |
|
68 | const caller = sites[2]
|
69 | const file = caller.filename
|
70 |
|
71 |
|
72 | const line = caller.line
|
73 | const column = caller.column
|
74 | const message = `
|
75 | file: ${file}
|
76 | line: ${line},
|
77 | column: ${column}
|
78 | `
|
79 | debug(message)
|
80 | let {specName, specSource, startLine} = getSpec({file, line, column})
|
81 |
|
82 |
|
83 |
|
84 | if (!specName) {
|
85 | const caller = sites[3]
|
86 | const file = caller.filename
|
87 | const line = caller.line
|
88 | const column = caller.column
|
89 | debug('trying to get snapshot from %s %d,%d', file, line, column)
|
90 | const out = getSpec({file, line, column})
|
91 | specName = out.specName
|
92 | specSource = out.specSource
|
93 | startLine = out.startLine
|
94 | }
|
95 |
|
96 | if (!specName) {
|
97 |
|
98 | const sites = callsites()
|
99 | const caller = sites[1]
|
100 | const file = caller.getFileName()
|
101 | const line = caller.getLineNumber()
|
102 | const column = caller.getColumnNumber()
|
103 | debug('trying to get snapshot from callsite %s %d,%d', file, line, column)
|
104 | const out = getSpec({file, line, column})
|
105 | specName = out.specName
|
106 | specSource = out.specSource
|
107 | startLine = out.startLine
|
108 | }
|
109 |
|
110 | if (!specName) {
|
111 | console.error('Problem finding caller')
|
112 | console.trace()
|
113 |
|
114 | const relativeName = fs.fromCurrentFolder(file)
|
115 | const msg = `Could not determine test for ${relativeName}
|
116 | line ${line} column ${column}`
|
117 | throw new Error(msg)
|
118 | }
|
119 | debug(`found spec name "${specName}" for line ${line} column ${column}`)
|
120 | la(is.unemptyString(specSource), 'could not get spec source from',
|
121 | file, 'line', line, 'column', column, 'named', specName)
|
122 | la(is.number(startLine), 'could not determine spec function start line',
|
123 | file, 'line', line, 'column', column, 'named', specName)
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 | if (is.fn(what) && arguments.length > 1) {
|
130 | debug(`data-driven test for ${what.name}`)
|
131 | la(is.unemptyString(what.name),
|
132 | 'input function is missing name', what.toString())
|
133 | what = dataDriven(what, Array.from(arguments).slice(1))
|
134 | }
|
135 |
|
136 | const setOrCheckValue = any => {
|
137 | const value = strip(any)
|
138 | snapShotCore({
|
139 | what: value,
|
140 | file,
|
141 | specName,
|
142 | compare: compare,
|
143 | ext: SNAP_SHOT_EXTENSION,
|
144 | opts
|
145 | })
|
146 | return value
|
147 | }
|
148 |
|
149 | if (is.promise(what)) {
|
150 | return what.then(setOrCheckValue)
|
151 | } else {
|
152 | return setOrCheckValue(what)
|
153 | }
|
154 | }
|
155 |
|
156 | if (isBrowser) {
|
157 |
|
158 | la(is.fn(fs.init), 'browser file system is missing init', fs)
|
159 | snapshot.init = fs.init
|
160 | }
|
161 |
|
162 | module.exports = snapshot
|