/
context.ts
149 lines (138 loc) · 3.76 KB
/
context.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import execa, { ExecaChildProcess } from 'execa'
import fs from 'fs-jetpack'
import { FSJetpack } from 'fs-jetpack/types'
import path from 'path'
import tempy from 'tempy'
/**
* Base test context.
*/
type BaseContext = {
tmpDir: string
fs: FSJetpack
mocked: {
cwd: string
}
/**
* Setup the temporary directory based on the contents of some fixture.
*/
fixture: (name: string) => void
/**
* Spawn the Prisma cli using the temporary directory as the CWD.
*
* @remarks
*
* For this to work the source must be built
*/
cli: (...input: string[]) => ExecaChildProcess<string>
}
/**
* Create test context to use in tests. Provides the following:
*
* - A temporary directory
* - an fs-jetpack instance bound to the temporary directory
* - Mocked process.cwd via Node process.chdir
* - Fixture loader for boostrapping the temporary directory with content
*/
export const Context = {
new: function (ctx: BaseContext = {} as any) {
const c = ctx as BaseContext
beforeEach(() => {
c.tmpDir = tempy.directory()
c.fs = fs.cwd(c.tmpDir)
c.fixture = (name: string) => {
// copy the fixture in isolated tmp directory
c.fs.copy(path.join(__dirname, '..', 'fixtures', name), '.', {
overwrite: true,
})
// symlink to local client version in tmp dir
c.fs.symlink(
path.join(__dirname, '..', '..', '..', '..', 'client'),
path.join(c.fs.cwd(), 'node_modules', '@prisma', 'client'),
)
}
c.mocked = c.mocked ?? {
cwd: process.cwd(),
}
c.cli = (...input) => {
return execa.node(
path.join(__dirname, '../../../build/index.js'),
input,
{
cwd: c.fs.cwd(),
stdio: 'pipe',
all: true,
},
)
}
process.chdir(c.tmpDir)
})
afterEach(() => {
process.chdir(c.mocked.cwd)
})
return factory(ctx)
},
}
/**
* Factory for creating a context contributor possibly configured in some special way.
*/
type ContextContributorFactory<Settings, Context, NewContext> =
Settings extends {}
? () => ContextContributor<Context, NewContext>
: (settings: Settings) => ContextContributor<Context, NewContext>
/**
* A function that provides additonal test context.
*/
type ContextContributor<Context, NewContext> = (ctx: Context) => NewContext
/**
* Main context builder API that permits recursively building up context.
*/
function factory<Context>(ctx: Context) {
return {
add<NewContext>(
contextContributor: ContextContributor<Context, NewContext>,
) {
contextContributor(ctx)
return factory<Context & NewContext>(ctx as any)
},
assemble(): Context {
return ctx
},
}
}
/**
* Test context contributor. Mocks console.error with a Jest spy before each test.
*/
export const consoleContext: ContextContributorFactory<
{},
BaseContext,
{
mocked: {
'console.error': jest.SpyInstance
'console.log': jest.SpyInstance
'console.info': jest.SpyInstance
'console.warn': jest.SpyInstance
}
}
> = () => (ctx) => {
beforeEach(() => {
ctx.mocked['console.error'] = jest
.spyOn(console, 'error')
.mockImplementation(() => {})
ctx.mocked['console.log'] = jest
.spyOn(console, 'log')
.mockImplementation(() => {})
ctx.mocked['console.info'] = jest
.spyOn(console, 'info')
.mockImplementation(() => {})
ctx.mocked['console.warn'] = jest
.spyOn(console, 'warn')
.mockImplementation(() => {})
})
afterEach(() => {
ctx.mocked['console.error'].mockRestore()
ctx.mocked['console.log'].mockRestore()
ctx.mocked['console.info'].mockRestore()
ctx.mocked['console.warn'].mockRestore()
})
return null as any
}