Skip to content

Commit da28833

Browse files
iHiDdem4ron
andauthored
Serialize initial code correctly (exercism#7862)
* Set initial code correctly * Start adding test cases --------- Co-authored-by: dem4ron <[email protected]>
1 parent 64c463a commit da28833

File tree

4 files changed

+141
-37
lines changed

4 files changed

+141
-37
lines changed

app/commands/bootcamp/solution/create.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ def code
4141
{
4242
html: Bootcamp::Solution::GenerateStub.(exercise, user, 'html'),
4343
css: Bootcamp::Solution::GenerateStub.(exercise, user, 'css')
44-
}
44+
}.to_json
4545
when "frontend"
4646
{
4747
html: Bootcamp::Solution::GenerateStub.(exercise, user, 'html'),
4848
css: Bootcamp::Solution::GenerateStub.(exercise, user, 'css'),
4949
js: Bootcamp::Solution::GenerateStub.(exercise, user, 'js')
50-
}
50+
}.to_json
5151
end
5252
end
5353

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { EditorCode } from './useSetupEditors'
2+
3+
export function getInitialEditorCode(code: CSSExercisePageCode): EditorCode {
4+
const fallbackReadonlyRanges = {
5+
html: code.defaultReadonlyRanges?.html || [],
6+
css: code.defaultReadonlyRanges?.css || [],
7+
}
8+
9+
const fallbackCode: EditorCode = {
10+
htmlEditorContent: code.stub.html,
11+
cssEditorContent: code.stub.css,
12+
storedAt: new Date().toISOString(),
13+
readonlyRanges: fallbackReadonlyRanges,
14+
}
15+
16+
if (!code.code) return fallbackCode
17+
18+
let parsed: { html?: string; css?: string } = {}
19+
try {
20+
parsed = JSON.parse(code.code)
21+
} catch (error) {
22+
console.error('Error parsing initial code:', error)
23+
return fallbackCode
24+
}
25+
26+
const html = parsed.html?.trim() || code.stub.html
27+
const css = parsed.css?.trim() || code.stub.css
28+
29+
return {
30+
htmlEditorContent: html,
31+
cssEditorContent: css,
32+
storedAt: new Date().toISOString(),
33+
readonlyRanges: fallbackReadonlyRanges,
34+
}
35+
}

app/javascript/components/bootcamp/CSSExercisePage/hooks/useSetupEditors.tsx

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useEditorHandler } from './useEditorHandler'
44
import { updateIFrame } from '../utils/updateIFrame'
55
import { EditorView } from 'codemirror'
66
import { updateReadOnlyRangesEffect } from '../../JikiscriptExercisePage/CodeMirror/extensions/read-only-ranges/readOnlyRanges'
7+
import { getInitialEditorCode } from './getInitialEditorCode'
78

89
export type ReadonlyRange = { from: number; to: number }
910

@@ -218,38 +219,3 @@ function resetSingleEditor(
218219
setupEditor(view, { code: '', readonlyRanges: [] })
219220
setupEditor(view, { code: content, readonlyRanges })
220221
}
221-
222-
function getInitialEditorCode(code: CSSExercisePageCode): EditorCode {
223-
const fallbackReadonlyRanges = {
224-
html: code.defaultReadonlyRanges?.html || [],
225-
css: code.defaultReadonlyRanges?.css || [],
226-
}
227-
228-
const fallbackCode: EditorCode = {
229-
htmlEditorContent: code.stub.html,
230-
cssEditorContent: code.stub.css,
231-
storedAt: new Date().toISOString(),
232-
readonlyRanges: fallbackReadonlyRanges,
233-
}
234-
235-
if (!code.code) return fallbackCode
236-
237-
let parsed: { html?: string; css?: string } = {}
238-
239-
try {
240-
parsed = JSON.parse(code.code)
241-
} catch (error) {
242-
console.error('Error parsing initial code:', error)
243-
return fallbackCode
244-
}
245-
246-
const html = parsed.html?.trim() || code.stub.html
247-
const css = parsed.css?.trim() || code.stub.css
248-
249-
return {
250-
htmlEditorContent: html,
251-
cssEditorContent: css,
252-
storedAt: new Date().toISOString(),
253-
readonlyRanges: fallbackReadonlyRanges,
254-
}
255-
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { getInitialEditorCode } from '@/components/bootcamp/CSSExercisePage/hooks/getInitialEditorCode'
2+
describe('getInitialEditorCode', () => {
3+
const baseStub = {
4+
stub: {
5+
html: '<div>Hello</div>',
6+
css: 'div { color: red; }',
7+
},
8+
code: undefined,
9+
defaultReadonlyRanges: {
10+
html: [{ start: 0, end: 5 }],
11+
css: [{ start: 0, end: 10 }],
12+
},
13+
}
14+
15+
it('returns fallback code when code.code is undefined', () => {
16+
const result = getInitialEditorCode(baseStub)
17+
expect(result).toMatchObject({
18+
htmlEditorContent: baseStub.stub.html,
19+
cssEditorContent: baseStub.stub.css,
20+
readonlyRanges: baseStub.defaultReadonlyRanges,
21+
})
22+
expect(typeof result.storedAt).toBe('string')
23+
})
24+
25+
it('returns fallback code and logs error when code.code is invalid JSON', () => {
26+
const invalidCode = { ...baseStub, code: '{ invalid JSON' }
27+
console.error = jest.fn()
28+
const result = getInitialEditorCode(invalidCode)
29+
expect(result).toMatchObject({
30+
htmlEditorContent: baseStub.stub.html,
31+
cssEditorContent: baseStub.stub.css,
32+
readonlyRanges: baseStub.defaultReadonlyRanges,
33+
})
34+
expect(console.error).toHaveBeenCalled()
35+
})
36+
37+
it('uses stub html/css if parsed code is missing both', () => {
38+
const input = {
39+
...baseStub,
40+
code: JSON.stringify({}),
41+
}
42+
const result = getInitialEditorCode(input)
43+
expect(result.htmlEditorContent).toBe(baseStub.stub.html)
44+
expect(result.cssEditorContent).toBe(baseStub.stub.css)
45+
})
46+
47+
it('uses stub html if parsed html is empty', () => {
48+
const input = {
49+
...baseStub,
50+
code: JSON.stringify({ html: '' }),
51+
}
52+
const result = getInitialEditorCode(input)
53+
expect(result.htmlEditorContent).toBe(baseStub.stub.html)
54+
})
55+
56+
it('uses stub css if parsed css is empty', () => {
57+
const input = {
58+
...baseStub,
59+
code: JSON.stringify({ css: '' }),
60+
}
61+
const result = getInitialEditorCode(input)
62+
expect(result.cssEditorContent).toBe(baseStub.stub.css)
63+
})
64+
65+
it('trims html/css content if provided in parsed code', () => {
66+
const input = {
67+
...baseStub,
68+
code: JSON.stringify({
69+
html: ' <p>Hi</p> ',
70+
css: ' p { color: blue; } ',
71+
}),
72+
}
73+
const result = getInitialEditorCode(input)
74+
expect(result.htmlEditorContent).toBe('<p>Hi</p>')
75+
expect(result.cssEditorContent).toBe('p { color: blue; }')
76+
})
77+
78+
it('handles missing defaultReadonlyRanges', () => {
79+
const input = {
80+
stub: baseStub.stub,
81+
code: undefined,
82+
defaultReadonlyRanges: undefined,
83+
}
84+
const result = getInitialEditorCode(input)
85+
expect(result.readonlyRanges).toEqual({ html: [], css: [] })
86+
})
87+
88+
it('handles partially defined defaultReadonlyRanges', () => {
89+
const input = {
90+
stub: baseStub.stub,
91+
code: undefined,
92+
defaultReadonlyRanges: {
93+
html: [{ start: 0, end: 3 }],
94+
// css is undefined
95+
},
96+
}
97+
const result = getInitialEditorCode(input)
98+
expect(result.readonlyRanges).toEqual({
99+
html: [{ start: 0, end: 3 }],
100+
css: [],
101+
})
102+
})
103+
})

0 commit comments

Comments
 (0)