forked from vercel/next.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rendering.js
149 lines (130 loc) · 5.34 KB
/
rendering.js
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
/* eslint-env jest */
import { join } from 'path'
import cheerio from 'cheerio'
import { check, File, waitFor } from 'next-test-utils'
export default function ({ app }, suiteName, render, fetch) {
async function get$(path, query) {
const html = await render(path, query)
return cheerio.load(html)
}
describe(suiteName, () => {
describe('_document', () => {
test('It has a custom html class', async () => {
const $ = await get$('/')
expect($('html').hasClass('test-html-props')).toBe(true)
})
test('It has a custom body class', async () => {
const $ = await get$('/')
expect($('body').hasClass('custom_class')).toBe(true)
})
it('Document.getInitialProps returns html prop representing app shell', async () => {
// Extract css-in-js-class from the rendered HTML, which is returned by Document.getInitialProps
const $index = await get$('/')
const $about = await get$('/about')
expect($index('#css-in-cjs-count').text()).toBe('2')
expect($about('#css-in-cjs-count').text()).toBe('0')
})
test('It injects custom head tags', async () => {
const $ = await get$('/')
expect($('head').text()).toMatch('body { margin: 0 }')
})
test('It passes props from Document.getInitialProps to Document', async () => {
const $ = await get$('/')
expect($('#custom-property').text()).toBe('Hello Document')
})
test('It adds nonces to all scripts and preload links', async () => {
const $ = await get$('/')
const nonce = 'test-nonce'
let noncesAdded = true
$('script, link[rel=preload]').each((index, element) => {
if ($(element).attr('nonce') !== nonce) noncesAdded = false
})
expect(noncesAdded).toBe(true)
})
test('It adds crossOrigin to all scripts and preload links', async () => {
const $ = await get$('/')
const crossOrigin = 'anonymous'
$('script, link[rel=preload]').each((index, element) => {
expect($(element).attr('crossorigin') === crossOrigin).toBeTruthy()
})
})
test('It renders ctx.renderPage with enhancer correctly', async () => {
const $ = await get$('/?withEnhancer=true')
const nonce = 'RENDERED'
expect($('#render-page-enhance-component').text().includes(nonce)).toBe(
true
)
})
test('It renders ctx.renderPage with enhanceComponent correctly', async () => {
const $ = await get$('/?withEnhanceComponent=true')
const nonce = 'RENDERED'
expect($('#render-page-enhance-component').text().includes(nonce)).toBe(
true
)
})
test('It renders ctx.renderPage with enhanceApp correctly', async () => {
const $ = await get$('/?withEnhanceApp=true')
const nonce = 'RENDERED'
expect($('#render-page-enhance-app').text().includes(nonce)).toBe(true)
})
test('It renders ctx.renderPage with enhanceApp and enhanceComponent correctly', async () => {
const $ = await get$('/?withEnhanceComponent=true&withEnhanceApp=true')
const nonce = 'RENDERED'
expect($('#render-page-enhance-app').text().includes(nonce)).toBe(true)
expect($('#render-page-enhance-component').text().includes(nonce)).toBe(
true
)
})
// This is a workaround to fix https://github.com/vercel/next.js/issues/5860
// TODO: remove this workaround when https://bugs.webkit.org/show_bug.cgi?id=187726 is fixed.
test('It adds a timestamp to link tags with preload attribute to invalidate the cache (DEV only)', async () => {
const $ = await get$('/')
$('link[rel=preload]').each((index, element) => {
const href = $(element).attr('href')
expect(href.match(/\?/g)).toHaveLength(1)
expect(href).toMatch(/\?ts=/)
})
$('script[src]').each((index, element) => {
const src = $(element).attr('src')
expect(src.match(/\?/g)).toHaveLength(1)
expect(src).toMatch(/\?ts=/)
})
})
})
describe('_app', () => {
test('It shows a custom tag', async () => {
const $ = await get$('/')
expect($('#hello-app').text()).toBe('Hello App')
})
// For example react context uses shared module state
// Also known as singleton modules
test('It should share module state with pages', async () => {
const $ = await get$('/shared')
expect($('#currentstate').text()).toBe('UPDATED')
})
test('It should show valid error when thrown in _app getInitialProps', async () => {
const errMsg = 'have an error from _app getInitialProps'
const _app = new File(join(__dirname, '../pages/_app.js'))
let foundErr = false
expect(await render('/')).toMatch('page-index')
_app.replace(
'// throw _app GIP err here',
`throw new Error("${errMsg}")`
)
try {
let tries = 0
while (!foundErr && tries < 5) {
foundErr = (await render('/')).indexOf(errMsg) > -1
await waitFor(1000)
tries++
}
} finally {
_app.restore()
// Make sure _app is restored
await check(() => render('/'), /page-index/)
expect(foundErr).toBeTruthy()
}
})
})
})
}