forked from vercel/next.js
/
helpers.ts
130 lines (120 loc) · 3.83 KB
/
helpers.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
import {
getRedboxDescription,
getRedboxHeader,
getRedboxSource,
hasErrorToast,
hasRedbox,
waitForAndOpenRuntimeError,
} from 'next-test-utils'
import webdriver from 'next-webdriver'
import { NextInstance } from 'test/lib/next-modes/base'
export async function sandbox(
next: NextInstance,
initialFiles?: Map<string, string>
) {
await next.stop()
await next.clean()
if (initialFiles) {
for (const [k, v] of initialFiles.entries()) {
await next.patchFile(k, v)
}
}
await next.start()
const browser = await webdriver(next.appPort, '/')
return {
browser,
session: {
async write(filename, content) {
// Update the file on filesystem
await next.patchFile(filename, content)
},
async patch(filename, content) {
// Register an event for HMR completion
await browser.eval(function () {
;(window as any).__HMR_STATE = 'pending'
var timeout = setTimeout(() => {
;(window as any).__HMR_STATE = 'timeout'
}, 30 * 1000)
;(window as any).__NEXT_HMR_CB = function () {
clearTimeout(timeout)
;(window as any).__HMR_STATE = 'success'
}
})
await this.write(filename, content)
for (;;) {
const status = await browser.eval(() => (window as any).__HMR_STATE)
if (!status) {
await new Promise((resolve) => setTimeout(resolve, 750))
// Wait for application to re-hydrate:
await browser.evalAsync(function () {
var callback = arguments[arguments.length - 1]
if ((window as any).__NEXT_HYDRATED) {
callback()
} else {
var timeout = setTimeout(callback, 30 * 1000)
;(window as any).__NEXT_HYDRATED_CB = function () {
clearTimeout(timeout)
callback()
}
}
})
console.log('Application re-loaded.')
// Slow down tests a bit:
await new Promise((resolve) => setTimeout(resolve, 750))
return false
}
if (status === 'success') {
console.log('Hot update complete.')
break
}
if (status !== 'pending') {
throw new Error(`Application is in inconsistent state: ${status}.`)
}
await new Promise((resolve) => setTimeout(resolve, 30))
}
// Slow down tests a bit (we don't know how long re-rendering takes):
await new Promise((resolve) => setTimeout(resolve, 750))
return true
},
async remove(filename) {
await next.deleteFile(filename)
},
async evaluate(snippet: () => any) {
if (typeof snippet === 'function') {
const result = await browser.eval(snippet)
await new Promise((resolve) => setTimeout(resolve, 30))
return result
} else {
throw new Error(
`You must pass a function to be evaluated in the browser.`
)
}
},
async hasRedbox(expected = false) {
return hasRedbox(browser, expected)
},
async hasErrorToast(expected = false) {
return hasErrorToast(browser, expected)
},
async waitForAndOpenRuntimeError() {
await waitForAndOpenRuntimeError(browser)
},
async getRedboxDescription() {
return getRedboxDescription(browser)
},
async getRedboxSource(includeHeader = false) {
const header = includeHeader ? await getRedboxHeader(browser) : ''
const source = await getRedboxSource(browser)
if (includeHeader) {
return `${header}\n\n${source}`
}
return source
},
},
async cleanup() {
await browser.close()
await next.stop()
await next.clean()
},
}
}