-
Notifications
You must be signed in to change notification settings - Fork 67k
Expand file tree
/
Copy pathliquid.ts
More file actions
309 lines (280 loc) · 11.8 KB
/
liquid.ts
File metadata and controls
309 lines (280 loc) · 11.8 KB
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
import { describe, expect, test } from 'vitest'
import type { CheerioAPI } from 'cheerio'
import { getDataByLanguage } from '@/data-directory/lib/get-data'
import { getDOM } from '@/tests/helpers/e2etest'
import { supported } from '@/versions/lib/enterprise-server-releases'
describe('spotlight', () => {
test('renders styled warnings', async () => {
const $: CheerioAPI = await getDOM('/get-started/liquid/warnings')
const nodes = $('.ghd-spotlight-attention')
expect(nodes.length).toBe(1)
expect(nodes.text().includes('This is inside the warning.')).toBe(true)
})
test('renders styled danger', async () => {
const $: CheerioAPI = await getDOM('/get-started/liquid/danger')
const nodes = $('.ghd-spotlight-danger')
expect(nodes.length).toBe(1)
expect(nodes.text().includes('Danger, Will Robinson.')).toBe(true)
})
test('renders styled tips', async () => {
const $: CheerioAPI = await getDOM('/get-started/liquid/tips')
const nodes = $('.ghd-spotlight-success')
expect(nodes.length).toBe(1)
expect(nodes.text().includes('This is inside the tip.')).toBe(true)
})
test('renders styled notes', async () => {
const $: CheerioAPI = await getDOM('/get-started/liquid/notes')
const nodes = $('.ghd-spotlight-accent')
expect(nodes.length).toBe(1)
expect(nodes.text().includes('This is inside the note.')).toBe(true)
})
})
describe('raw', () => {
test('renders raw', async () => {
const $: CheerioAPI = await getDOM('/get-started/liquid/raw')
const lead = $('[data-testid="lead"]').html()
expect(lead).toMatch('{% raw %}')
const code = $('pre code').html()
expect(code).toMatch('{% data foo.bar.buzz %}')
expect(code).toMatch('{{ page.title }}')
})
})
describe('tool', () => {
test('renders platform-specific content', async () => {
const $: CheerioAPI = await getDOM('/get-started/liquid/platform-specific')
expect($('.ghd-tool.mac p').length).toBe(1)
expect($('.ghd-tool.mac p').text().includes('mac specific content')).toBe(true)
expect($('.ghd-tool.windows p').length).toBe(1)
expect($('.ghd-tool.windows p').text().includes('windows specific content')).toBe(true)
expect($('.ghd-tool.linux p').length).toBe(1)
expect($('.ghd-tool.linux p').text().includes('linux specific content')).toBe(true)
})
test('renders expected mini TOC headings in platform-specific content', async () => {
const $: CheerioAPI = await getDOM('/get-started/liquid/platform-specific')
expect($('h2#in-this-article').length).toBe(1)
expect($('h2#in-this-article + nav ul .ghd-tool.mac').length).toBe(1)
expect($('h2#in-this-article + nav ul .ghd-tool.windows').length).toBe(1)
expect($('h2#in-this-article + nav ul .ghd-tool.linux').length).toBe(1)
})
})
describe('post', () => {
test('whitespace control', async () => {
const $: CheerioAPI = await getDOM('/get-started/liquid/whitespace')
const html = $('#article-contents').html()
expect(html).toMatch('<p>HubGit</p>')
expect(html).toMatch('<p>Text before. HubGit Text after.</p>')
expect(html).toMatch('<li>HubGit</li>')
expect(html).toMatch('CramFPTped')
// Test what happens to `Cram{% ifversion fpt %}FPT{% endif %}ped.`
// when it's not free-pro-team.
{
const $inner: CheerioAPI = await getDOM(
'/enterprise-server@latest/get-started/liquid/whitespace',
)
const innerHtml = $inner('#article-contents').html()
// Assures that there's not whitespace left when the `{% ifversion %}`
// yields an empty string.
expect(innerHtml).toMatch('Cramped')
}
})
})
describe('rowheaders', () => {
test('rowheaders', async () => {
const $: CheerioAPI = await getDOM('/get-started/liquid/table-row-headers')
const tables = $('#article-contents table')
expect(tables.length).toBe(2)
// The first table should have this structure:
//
// table
// tbody
// tr
// th
// td
// td
// td
//
// (and there are 2 of these <tr> rows)
//
// That's because a Liquid + Markdown solution rewrites the
// *first* `tbody td` to become a `th` instead.
const firstTable = tables.filter((i: number) => i === 0)
expect($('tbody tr th', firstTable).length).toBe(2)
expect($('tbody tr td', firstTable).length).toBe(2 * 3)
// The second table should have this structure:
//
// table
// tbody
// tr
// td
// td
// td
//
// (and there are 3 of these <tr> rows)
const secondTable = tables.filter((i: number) => i === 1)
expect($('tbody tr th', secondTable).length).toBe(0)
expect($('tbody tr td', secondTable).length).toBe(3 * 3)
// More specifically, the <th> tags should have the appropriate
// `scope` attribute.
// See "Scope attribute should be used correctly on tables"
// https://dequeuniversity.com/rules/axe/4.1/scope-attr-valid?application=RuleDescription
$('thead th', firstTable).each((i, element) => {
expect($(element).attr('scope')).toBe('col')
})
$('tbody th', firstTable).each((i, element) => {
expect($(element).attr('scope')).toBe('row')
})
// The 5 here is the other `expect(...)` that happens before these
// two, just above, `expect(...)` inside the `.each(...)` loops.
let totalAssertions = 5
totalAssertions += $('thead th', firstTable).length
totalAssertions += $('tbody th', firstTable).length
expect.assertions(totalAssertions)
})
})
describe('ifversion', () => {
// the matchesPerVersion object contains a list of conditions that
// should match per version tested, but we also operate against it
// to find out versions that shouldn't match
const ghesLast = `enterprise-server@${supported[supported.length - 1]}`
const ghesPenultimate = `enterprise-server@${supported[supported.length - 2]}`
const matchesPerVersion: Record<string, string[]> = {
'free-pro-team@latest': [
'condition-a',
'condition-b',
'condition-d',
'condition-i',
'condition-j',
'condition-l',
],
'enterprise-cloud@latest': ['condition-c', 'condition-j', 'condition-l'],
[ghesLast]: [
'condition-c',
'condition-e',
'condition-f',
'condition-g',
'condition-h',
'condition-i',
'condition-k',
'condition-m',
'condition-n',
'condition-o',
],
[ghesPenultimate]: [
'condition-c',
'condition-e',
'condition-f',
'condition-i',
'condition-j',
'condition-m',
'condition-o',
],
}
test.each(Object.keys(matchesPerVersion))(
'ifversion using rendered version %p',
async (version: string) => {
const $: CheerioAPI = await getDOM(`/${version}/get-started/liquid/ifversion`)
const html = $('#article-contents').html()
const allConditions = Object.values(matchesPerVersion).flat()
// this is all conditions that should match for this rendered version
const wantedConditions = allConditions.filter((condition: string) => {
return matchesPerVersion[version].includes(condition)
})
// this is the inverse of the above, conditions that shouldn't match for this rendered version
const unwantedConditions = allConditions.filter((condition: string) => {
return !matchesPerVersion[version].includes(condition)
})
for (const condition of wantedConditions as string[]) {
expect(html).toMatch(condition)
}
for (const condition of unwantedConditions as string[]) {
expect(html).not.toMatch(condition)
}
},
)
})
describe('misc Liquid', () => {
test('links with liquid from data', async () => {
const $: CheerioAPI = await getDOM('/get-started/liquid/links-with-liquid')
// The URL comes from variables.product.pricing_url
const url = getDataByLanguage('variables.product.pricing_url', 'en')
if (!url) throw new Error('variable could not be found')
const links = $(`#article-contents a[href="${url}"]`)
expect(links.length).toBe(2)
const texts = links
.map((i, element) => {
return $(element).text()
})
.get()
expect(texts[0]).toBe(url)
expect(texts[1]).toBe('Pricing')
})
test('page with tool Liquid tag followed by Markdown', async () => {
// This test tests Markdown being correctly rendered when the
// Markdown directly follows a tool tag like `{% linux %}...{% endlinux %}`.
// The next line immediately after the `{% endlinux %}` should not
// leave the Markdown unrendered
const $: CheerioAPI = await getDOM('/get-started/liquid/tool-platform-switcher')
const innerHTML = $('#article-contents').html()
expect(innerHTML).not.toMatch('On *this* line is `Markdown` too.')
expect(innerHTML).toMatch('<p>On <em>this</em> line is <code>Markdown</code> too.</p>')
})
})
describe('data tag', () => {
test('injects data reusables with the right whitespace', async () => {
const $: CheerioAPI = await getDOM('/get-started/liquid/data')
// This proves that the two injected reusables tables work.
// CommonMark is finicky if the indentation isn't perfect, so
// if you don't get exactly 2 tables, something is wrong, and if it's
// wrong it's most likely because of the leading whitespaces.
expect($('#article-contents table').length).toBe(2)
// To truly understand this test, you have to see
// http://localhost:4000/en/get-started/liquid/data to understand it.
// The page uses `{% data ... %}` within the bodies of bullet points.
// If the whitespace isn't correct and working, the bullet points
// would get confused and think the bullet point "body" is a new
// bullet point on its own.
expect($('#article-contents ol').length).toBe(3)
expect($('#article-contents ol li').length).toBe(2 + 1 + 2)
// In the very first bullet point we inject something that multiple
// linebreaks in it. The source looks like this:
//
// 1. Bullet point
//
// {% data reusables.injectables.multiple_numbers %}
//
// (The code comment itself here has 3 spaces of manual indentation)
// What's important is that all the expected lines of that reusables
// stick inside this `ul li` block.
const liText = $('#article-contents ol li').first().text()
expect(liText).toMatch(/Bullet point\nOne\nTwo\nThree\nFour/)
// The code block uses `{% data ... %}` and it should be indented
// so that it aligns perfectly with the code block itself.
// One of the injected data reusables contains multiple lines.
// It's important that each line from that starts at the far
// left. No more or less whitespace.
const codeBlock = $('#article-contents li pre').text()
expect(codeBlock).toMatch(/^One\n/)
expect(codeBlock).toMatch(/^One\nTwo\n/)
expect(codeBlock).toMatch(/^One\nTwo\nThree\n/)
// The code block also a reusables that is just one line.
expect(codeBlock).toMatch(/One Two Three Four\n/)
// On its own, if you look at
// src/fixtures/fixtures/data/reusables/injectables/paragraphs.md, you'll
// see each line is NOT prefixed with whitespace indentation.
// But because `{% data reusables.injectables.paragraphs %}` is
// inserted with some indentation, that's replicated on every line.
const li = $('#article-contents li')
.filter((_, element) => {
return $(element).text().trim().startsWith('Point 1')
})
.eq(0)
// You can't really test the exact whitespace with cheerio,
// of the original HTML, but it doesn't actually matter. What
// matters is that within the bullet point, that starts with "Point 1",
// it *contains* all the paragraphs
// from src/fixtures/fixtures/data/reusables/injectables/paragraphs.md.
expect(li.text()).toMatch(/Paragraph one/)
expect(li.text()).toMatch(/Paragraph two/)
expect(li.text()).toMatch(/Paragraph three/)
})
})