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
310
311
312
313
314
315
316
|
# frozen_string_literal: true
require 'fast_spec_helper'
require_relative '../../../../scripts/lib/glfm/update_example_snapshots'
RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
subject { described_class.new }
# GLFM input files
let(:glfm_spec_txt_path) { described_class::GLFM_SPEC_TXT_PATH }
let(:glfm_spec_txt_local_io) { StringIO.new(glfm_spec_txt_contents) }
let(:glfm_example_status_yml_path) { described_class::GLFM_EXAMPLE_STATUS_YML_PATH }
let(:glfm_example_status_yml_io) { StringIO.new(glfm_example_status_yml_contents) }
# Example Snapshot (ES) output files
let(:es_examples_index_yml_path) { described_class::ES_EXAMPLES_INDEX_YML_PATH }
let(:es_examples_index_yml_io) { StringIO.new }
let(:es_markdown_yml_path) { described_class::ES_MARKDOWN_YML_PATH }
let(:es_markdown_yml_io) { StringIO.new }
let(:es_html_yml_path) { described_class::ES_HTML_YML_PATH }
let(:es_html_yml_io) { StringIO.new }
let(:es_prosemirror_json_yml_path) { described_class::ES_PROSEMIRROR_JSON_YML_PATH }
let(:es_prosemirror_json_yml_io) { StringIO.new }
# Internal tempfiles
let(:static_html_tempfile_path) { Tempfile.new.path }
let(:glfm_spec_txt_contents) do
<<~GLFM_SPEC_TXT_CONTENTS
---
title: GitLab Flavored Markdown Spec
...
# Introduction
GLFM intro text...
# Inlines
## Strong
```````````````````````````````` example
__bold__
.
<p><strong>bold</strong></p>
````````````````````````````````
```````````````````````````````` example strikethrough
__bold with more text__
.
<p><strong>bold with more text</strong></p>
````````````````````````````````
<div class="extension">
## Strikethrough (extension)
GFM enables the `strikethrough` extension.
```````````````````````````````` example strikethrough
~~Hi~~ Hello, world!
.
<p><del>Hi</del> Hello, world!</p>
````````````````````````````````
</div>
End of last GitHub examples section.
# First GitLab-Specific Section with Examples
## Strong but with two asterisks
```````````````````````````````` example gitlab strong
**bold**
.
<p><strong>bold</strong></p>
````````````````````````````````
# Second GitLab-Specific Section with Examples
## Strong but with HTML
```````````````````````````````` example gitlab strong
<strong>
bold
</strong>
.
<p><strong>
bold
</strong></p>
````````````````````````````````
<!-- END TESTS -->
# Appendix
Appendix text.
GLFM_SPEC_TXT_CONTENTS
end
let(:glfm_example_status_yml_contents) do
<<~GLFM_EXAMPLE_STATUS_YML_CONTENTS
---
- 07_01_first_gitlab_specific_section_with_examples_strong_but_with_two_asterisks:
skip_update_example_snapshots: false
skip_running_snapshot_static_html_tests: false
skip_running_snapshot_wysiwyg_html_tests: false
skip_running_snapshot_prosemirror_json_tests: false
skip_running_conformance_static_tests: false
skip_running_conformance_wysiwyg_tests: false
- 07_02_first_gitlab_specific_section_with_examples_strong_but_with_html:
skip_update_example_snapshots: false
skip_running_snapshot_static_html_tests: false
skip_running_snapshot_wysiwyg_html_tests: false
skip_running_snapshot_prosemirror_json_tests: false
skip_running_conformance_static_tests: false
skip_running_conformance_wysiwyg_tests: false
GLFM_EXAMPLE_STATUS_YML_CONTENTS
end
before do
# We mock out the URI and local file IO objects with real StringIO, instead of just mock
# objects. This gives better and more realistic coverage, while still avoiding
# actual network and filesystem I/O during the spec run.
# input files
allow(File).to receive(:open).with(glfm_spec_txt_path) { glfm_spec_txt_local_io }
allow(File).to receive(:open).with(glfm_example_status_yml_path) { glfm_example_status_yml_io }
# output files
allow(File).to receive(:open).with(es_examples_index_yml_path, 'w') { es_examples_index_yml_io }
allow(File).to receive(:open).with(es_html_yml_path, 'w') { es_html_yml_io }
allow(File).to receive(:open).with(es_prosemirror_json_yml_path, 'w') { es_prosemirror_json_yml_io }
# output files which are also input files
allow(File).to receive(:open).with(es_markdown_yml_path, 'w') { es_markdown_yml_io }
allow(File).to receive(:open).with(es_markdown_yml_path) { es_markdown_yml_io }
# Allow normal opening of Tempfile files created during script execution.
tempfile_basenames = [
described_class::MARKDOWN_TEMPFILE_BASENAME[0],
described_class::STATIC_HTML_TEMPFILE_BASENAME[0],
described_class::WYSIWYG_HTML_AND_JSON_TEMPFILE_BASENAME[0]
].join('|')
# NOTE: This approach with a single regex seems to be the only way this can work. If you
# attempt to have multiple `allow...and_call_original` with `any_args`, the mocked
# parameter matching will fail to match the second one.
tempfiles_regex = /(#{tempfile_basenames})/
allow(File).to receive(:open).with(tempfiles_regex, any_args).and_call_original
# Prevent console output when running tests
allow(subject).to receive(:output)
end
describe 'writing examples_index.yml' do
let(:es_examples_index_yml_contents) { reread_io(es_examples_index_yml_io) }
it 'writes the correct content' do
subject.process(skip_static_and_wysiwyg: true)
expected =
<<~ES_EXAMPLES_INDEX_YML_CONTENTS
---
02_01__inlines__strong__01:
spec_txt_example_position: 1
source_specification: commonmark
02_01__inlines__strong__02:
spec_txt_example_position: 2
source_specification: github
02_02__inlines__strikethrough_extension__01:
spec_txt_example_position: 3
source_specification: github
03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01:
spec_txt_example_position: 4
source_specification: gitlab
04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__01:
spec_txt_example_position: 5
source_specification: gitlab
ES_EXAMPLES_INDEX_YML_CONTENTS
expect(es_examples_index_yml_contents).to eq(expected)
end
end
describe 'writing markdown.yml' do
let(:es_markdown_yml_contents) { reread_io(es_markdown_yml_io) }
it 'writes the correct content' do
subject.process(skip_static_and_wysiwyg: true)
expected =
<<~ES_MARKDOWN_YML_CONTENTS
---
02_01__inlines__strong__01: |
__bold__
02_01__inlines__strong__02: |
__bold with more text__
02_02__inlines__strikethrough_extension__01: |
~~Hi~~ Hello, world!
03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01: |
**bold**
04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__01: |
<strong>
bold
</strong>
ES_MARKDOWN_YML_CONTENTS
expect(es_markdown_yml_contents).to eq(expected)
end
end
describe 'writing html.yml and prosemirror_json.yml' do
let(:es_html_yml_contents) { reread_io(es_html_yml_io) }
let(:es_prosemirror_json_yml_contents) { reread_io(es_prosemirror_json_yml_io) }
let(:glfm_example_status_yml_contents) do
<<~GLFM_EXAMPLE_STATUS_YML_CONTENTS
---
- 02_01_gitlab_specific_section_with_examples_strong_but_with_two_asterisks:
skip_update_example_snapshots: false
skip_running_snapshot_static_html_tests: false
skip_running_snapshot_wysiwyg_html_tests: false
skip_running_snapshot_prosemirror_json_tests: false
skip_running_conformance_static_tests: false
skip_running_conformance_wysiwyg_tests: false
GLFM_EXAMPLE_STATUS_YML_CONTENTS
end
let(:glfm_spec_txt_contents) do
<<~GLFM_SPEC_TXT_CONTENTS
---
title: GitLab Flavored Markdown Spec
...
# Introduction
# GitLab-Specific Section with Examples
## Strong but with two asterisks
```````````````````````````````` example gitlab strong
**bold**
.
<p><strong>bold</strong></p>
````````````````````````````````
<!-- END TESTS -->
# Appendix
Appendix text.
GLFM_SPEC_TXT_CONTENTS
end
before do
# NOTE: This is a necessary to avoid an `error Couldn't find an integrity file` error
# when invoking `yarn jest ...` on CI from within an RSpec job. It could be solved by
# adding `.yarn-install` to be included in the RSpec CI job, but that would be a performance
# hit to all RSpec jobs. We could also make a dedicate job just for this spec. However,
# since this is just a single script, those options may not be justified.
described_class.new.run_external_cmd('yarn install') if ENV['CI']
end
# NOTE: Both `html.yml` and `prosemirror_json.yml` generation are tested in a single example, to
# avoid slower tests, because generating the static HTML is slow due to the need to invoke
# the rails environment. We could have separate sections, but this would require an extra flag
# to the `process` method to independently skip static vs. WYSIWYG, which is not worth the effort.
it 'writes the correct content' do
subject.process
expected_html =
<<~ES_HTML_YML_CONTENTS
---
02_01__gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01:
canonical: |
<p><strong>bold</strong></p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto"><strong>bold</strong></p>
wysiwyg: |-
<p><strong>bold</strong></p>
ES_HTML_YML_CONTENTS
expected_prosemirror_json =
<<~ES_PROSEMIRROR_JSON_YML_CONTENTS
---
02_01__gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01: |-
{
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"marks": [
{
"type": "bold"
}
],
"text": "bold"
}
]
}
]
}
ES_PROSEMIRROR_JSON_YML_CONTENTS
expect(es_html_yml_contents).to eq(expected_html)
expect(es_prosemirror_json_yml_contents).to eq(expected_prosemirror_json)
end
end
def reread_io(io)
# Reset the io StringIO to the beginning position of the buffer
io.seek(0)
io.read
end
end
|