@instructure/ui-source-code-editor 10.16.1 → 10.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/es/SourceCodeEditor/__new-tests__/SourceCodeEditor.test.js +241 -4
- package/lib/SourceCodeEditor/__new-tests__/SourceCodeEditor.test.js +240 -2
- package/package.json +16 -18
- package/src/SourceCodeEditor/__new-tests__/SourceCodeEditor.test.tsx +302 -2
- package/tsconfig.build.json +0 -3
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/SourceCodeEditor/__new-tests__/SourceCodeEditor.test.d.ts.map +1 -1
- package/es/SourceCodeEditor/SourceCodeEditorLocator.js +0 -58
- package/lib/SourceCodeEditor/SourceCodeEditorLocator.js +0 -63
- package/src/SourceCodeEditor/SourceCodeEditorLocator.ts +0 -59
- package/types/SourceCodeEditor/SourceCodeEditorLocator.d.ts +0 -355
- package/types/SourceCodeEditor/SourceCodeEditorLocator.d.ts.map +0 -1
|
@@ -21,10 +21,13 @@
|
|
|
21
21
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
22
|
* SOFTWARE.
|
|
23
23
|
*/
|
|
24
|
+
import { render, screen, waitFor } from '@testing-library/react'
|
|
25
|
+
import userEvent from '@testing-library/user-event'
|
|
26
|
+
import { vi } from 'vitest'
|
|
27
|
+
import type { MockInstance } from 'vitest'
|
|
24
28
|
|
|
25
|
-
import { render } from '@testing-library/react'
|
|
26
|
-
import SourceCodeEditor from '../index'
|
|
27
29
|
import '@testing-library/jest-dom'
|
|
30
|
+
import SourceCodeEditor from '../index'
|
|
28
31
|
|
|
29
32
|
describe('<SourceCodeEditor />', () => {
|
|
30
33
|
describe('syntax highlight', () => {
|
|
@@ -59,4 +62,301 @@ describe('<SourceCodeEditor />', () => {
|
|
|
59
62
|
expect(editorElement).toHaveAttribute('aria-labelledby', labelId)
|
|
60
63
|
})
|
|
61
64
|
})
|
|
65
|
+
|
|
66
|
+
describe('defaultValue', () => {
|
|
67
|
+
let consoleWarningMock: ReturnType<typeof vi.spyOn>
|
|
68
|
+
let consoleErrorMock: ReturnType<typeof vi.spyOn>
|
|
69
|
+
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
// Mocking console to prevent test output pollution
|
|
72
|
+
consoleWarningMock = vi
|
|
73
|
+
.spyOn(console, 'warn')
|
|
74
|
+
.mockImplementation(() => {}) as MockInstance
|
|
75
|
+
consoleErrorMock = vi
|
|
76
|
+
.spyOn(console, 'error')
|
|
77
|
+
.mockImplementation(() => {}) as MockInstance
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
afterEach(() => {
|
|
81
|
+
consoleWarningMock.mockRestore()
|
|
82
|
+
consoleErrorMock.mockRestore()
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('should be applied on load', async () => {
|
|
86
|
+
render(<SourceCodeEditor label="foo" defaultValue="hello" />)
|
|
87
|
+
const input = screen.getByRole('textbox')
|
|
88
|
+
|
|
89
|
+
expect(input).toHaveTextContent('hello')
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
describe('spellcheck', () => {
|
|
94
|
+
it('should set `spellcheck="true"` on the input', async () => {
|
|
95
|
+
render(<SourceCodeEditor label="foo" spellcheck />)
|
|
96
|
+
const input = screen.getByRole('textbox')
|
|
97
|
+
|
|
98
|
+
expect(input).toHaveAttribute('spellcheck', 'true')
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
describe('readOnly', () => {
|
|
103
|
+
it('should still update value when value prop changes', async () => {
|
|
104
|
+
const onChange = vi.fn()
|
|
105
|
+
const { rerender } = render(
|
|
106
|
+
<SourceCodeEditor
|
|
107
|
+
label="foo"
|
|
108
|
+
readOnly
|
|
109
|
+
value="hello"
|
|
110
|
+
onChange={onChange}
|
|
111
|
+
/>
|
|
112
|
+
)
|
|
113
|
+
const input = screen.getByRole('textbox')
|
|
114
|
+
expect(input).not.toHaveTextContent('hello world')
|
|
115
|
+
|
|
116
|
+
rerender(
|
|
117
|
+
<SourceCodeEditor
|
|
118
|
+
label="foo"
|
|
119
|
+
readOnly
|
|
120
|
+
value="hello world"
|
|
121
|
+
onChange={onChange}
|
|
122
|
+
/>
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
const inputUpdated = screen.getByRole('textbox')
|
|
126
|
+
expect(inputUpdated).toHaveTextContent('hello world')
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('should still be focusable', async () => {
|
|
130
|
+
let elementRef: SourceCodeEditor | null = null
|
|
131
|
+
|
|
132
|
+
render(
|
|
133
|
+
<SourceCodeEditor
|
|
134
|
+
label="foo"
|
|
135
|
+
readOnly
|
|
136
|
+
ref={(component: SourceCodeEditor) => {
|
|
137
|
+
elementRef = component
|
|
138
|
+
}}
|
|
139
|
+
/>
|
|
140
|
+
)
|
|
141
|
+
const input = screen.getByRole('textbox')
|
|
142
|
+
|
|
143
|
+
elementRef!.focus()
|
|
144
|
+
|
|
145
|
+
await waitFor(() => {
|
|
146
|
+
expect(input).toHaveFocus()
|
|
147
|
+
expect(document.activeElement).toBe(input)
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
describe('editable turned off', () => {
|
|
153
|
+
it('should set `contenteditable` to false', async () => {
|
|
154
|
+
render(<SourceCodeEditor label="foo" editable={false} />)
|
|
155
|
+
|
|
156
|
+
const input = screen.getByRole('textbox')
|
|
157
|
+
expect(input).toHaveAttribute('contenteditable', 'false')
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('should not be focusable', async () => {
|
|
161
|
+
let elementRef: HTMLDivElement | null = null
|
|
162
|
+
|
|
163
|
+
render(
|
|
164
|
+
<SourceCodeEditor
|
|
165
|
+
label="foo"
|
|
166
|
+
editable={false}
|
|
167
|
+
elementRef={(component: HTMLDivElement | null) => {
|
|
168
|
+
elementRef = component
|
|
169
|
+
}}
|
|
170
|
+
/>
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
const input = screen.getByRole('textbox')
|
|
174
|
+
|
|
175
|
+
elementRef!.focus()
|
|
176
|
+
|
|
177
|
+
await waitFor(() => {
|
|
178
|
+
expect(elementRef).not.toHaveFocus()
|
|
179
|
+
expect(document.activeElement).not.toBe(input)
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
describe('lineNumbers', () => {
|
|
185
|
+
it('should display line numbers', async () => {
|
|
186
|
+
const { container } = render(
|
|
187
|
+
<SourceCodeEditor
|
|
188
|
+
label="foo"
|
|
189
|
+
defaultValue={`
|
|
190
|
+
line1
|
|
191
|
+
line2
|
|
192
|
+
line3
|
|
193
|
+
`}
|
|
194
|
+
lineNumbers
|
|
195
|
+
/>
|
|
196
|
+
)
|
|
197
|
+
const lineNumbers = container.querySelector('[class$="-lineNumbers"]')!
|
|
198
|
+
|
|
199
|
+
expect(lineNumbers).toBeInTheDocument()
|
|
200
|
+
expect(lineNumbers).toBeVisible()
|
|
201
|
+
expect(lineNumbers).toHaveTextContent('123')
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
describe('foldGutter', () => {
|
|
206
|
+
it('should display fold icons', async () => {
|
|
207
|
+
render(
|
|
208
|
+
<SourceCodeEditor
|
|
209
|
+
label="foo"
|
|
210
|
+
defaultValue={`const func = () => {
|
|
211
|
+
console.log('foo')
|
|
212
|
+
}`}
|
|
213
|
+
foldGutter
|
|
214
|
+
/>
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
const gutterIcon = screen.getByTitle('Fold line')
|
|
218
|
+
|
|
219
|
+
expect(gutterIcon).toBeInTheDocument()
|
|
220
|
+
expect(gutterIcon).toBeVisible()
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('should fold lines on click', async () => {
|
|
224
|
+
const { container } = render(
|
|
225
|
+
<SourceCodeEditor
|
|
226
|
+
label="foo"
|
|
227
|
+
defaultValue={`const func = () => {
|
|
228
|
+
console.log('foo')
|
|
229
|
+
}`}
|
|
230
|
+
foldGutter
|
|
231
|
+
/>
|
|
232
|
+
)
|
|
233
|
+
const editor = container.querySelector('[class$="-codeEditor"]')
|
|
234
|
+
const gutterIcon = screen.getByTitle('Fold line')
|
|
235
|
+
|
|
236
|
+
expect(gutterIcon).toBeInTheDocument()
|
|
237
|
+
|
|
238
|
+
await userEvent.click(gutterIcon)
|
|
239
|
+
|
|
240
|
+
const unfoldIcons = screen.getAllByTitle('Unfold line')
|
|
241
|
+
|
|
242
|
+
expect(editor).not.toHaveTextContent("console.log('foo')")
|
|
243
|
+
expect(unfoldIcons[1]).toBeVisible()
|
|
244
|
+
})
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
describe('highlightActiveLine', () => {
|
|
248
|
+
it('should not highlight line by default', async () => {
|
|
249
|
+
const { container } = render(
|
|
250
|
+
<SourceCodeEditor label="foo" defaultValue={`const myNumber = 8`} />
|
|
251
|
+
)
|
|
252
|
+
const allLines = container.querySelectorAll('[class="cm-line"]')!
|
|
253
|
+
|
|
254
|
+
expect(allLines[0]).not.toHaveClass('cm-activeLine')
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it('should highlight line when true', async () => {
|
|
258
|
+
const { container } = render(
|
|
259
|
+
<SourceCodeEditor
|
|
260
|
+
label="foo"
|
|
261
|
+
defaultValue={`const myNumber = 8`}
|
|
262
|
+
highlightActiveLine
|
|
263
|
+
/>
|
|
264
|
+
)
|
|
265
|
+
const allLines = container.querySelectorAll('[class$="cm-line"]')!
|
|
266
|
+
|
|
267
|
+
expect(allLines[0]).toHaveClass('cm-activeLine')
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
describe('highlightActiveLineGutter', () => {
|
|
272
|
+
it('should not highlight gutter element by default', async () => {
|
|
273
|
+
const { container } = render(
|
|
274
|
+
<SourceCodeEditor
|
|
275
|
+
label="foo"
|
|
276
|
+
defaultValue={`const myNumber = 8`}
|
|
277
|
+
lineNumbers
|
|
278
|
+
/>
|
|
279
|
+
)
|
|
280
|
+
const allGutterElements = container.querySelectorAll(
|
|
281
|
+
'[class$="cm-gutterElement"]'
|
|
282
|
+
)!
|
|
283
|
+
|
|
284
|
+
expect(allGutterElements[0]).not.toHaveClass('cm-activeLineGutter')
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
it('should highlight gutter element when true', async () => {
|
|
288
|
+
const { container } = render(
|
|
289
|
+
<SourceCodeEditor
|
|
290
|
+
label="foo"
|
|
291
|
+
defaultValue={`const myNumber = 8`}
|
|
292
|
+
lineNumbers
|
|
293
|
+
highlightActiveLineGutter
|
|
294
|
+
/>
|
|
295
|
+
)
|
|
296
|
+
const allGutterElements = container.querySelectorAll(
|
|
297
|
+
'[class^="cm-gutterElement"]'
|
|
298
|
+
)!
|
|
299
|
+
|
|
300
|
+
expect(allGutterElements[1]).toHaveClass('cm-activeLineGutter')
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
describe('direction', () => {
|
|
305
|
+
it('rtl should apply', async () => {
|
|
306
|
+
render(
|
|
307
|
+
<SourceCodeEditor label="foo" defaultValue="hello" direction={'rtl'} />
|
|
308
|
+
)
|
|
309
|
+
const input = screen.getByRole('textbox')
|
|
310
|
+
|
|
311
|
+
expect(input).toHaveAttribute('dir', 'rtl')
|
|
312
|
+
})
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
describe('label', () => {
|
|
316
|
+
it('should be inserted in the ScreenReaderContent', async () => {
|
|
317
|
+
const { container } = render(
|
|
318
|
+
<SourceCodeEditor
|
|
319
|
+
label="this is a label for the SR"
|
|
320
|
+
defaultValue="hello"
|
|
321
|
+
/>
|
|
322
|
+
)
|
|
323
|
+
const label = container.querySelector('[class$="-screenReaderContent"]')
|
|
324
|
+
|
|
325
|
+
expect(label).toHaveTextContent('this is a label for the SR')
|
|
326
|
+
})
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
describe('elementRef', () => {
|
|
330
|
+
it('should return with the root element', async () => {
|
|
331
|
+
const elementRef = vi.fn()
|
|
332
|
+
const { container } = render(
|
|
333
|
+
<SourceCodeEditor
|
|
334
|
+
label="foo"
|
|
335
|
+
defaultValue="hello"
|
|
336
|
+
elementRef={elementRef}
|
|
337
|
+
/>
|
|
338
|
+
)
|
|
339
|
+
const editor = container.querySelector('[class$="-codeEditor"]')
|
|
340
|
+
|
|
341
|
+
expect(elementRef).toHaveBeenCalledWith(editor)
|
|
342
|
+
})
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
describe('containerRef', () => {
|
|
346
|
+
it('should return with the root element', async () => {
|
|
347
|
+
const containerRef = vi.fn()
|
|
348
|
+
const { container } = render(
|
|
349
|
+
<SourceCodeEditor
|
|
350
|
+
label="foo"
|
|
351
|
+
defaultValue="hello"
|
|
352
|
+
containerRef={containerRef}
|
|
353
|
+
/>
|
|
354
|
+
)
|
|
355
|
+
const editorContainer = container.querySelector(
|
|
356
|
+
'[class$="-codeEditorContainer"]'
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
expect(containerRef).toHaveBeenCalledWith(editorContainer)
|
|
360
|
+
})
|
|
361
|
+
})
|
|
62
362
|
})
|
package/tsconfig.build.json
CHANGED
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
{ "path": "../ui-buttons/tsconfig.build.json" },
|
|
12
12
|
{ "path": "../ui-text-input/tsconfig.build.json" },
|
|
13
13
|
{ "path": "../ui-icons/tsconfig.build.json" },
|
|
14
|
-
{ "path": "../ui-test-utils/tsconfig.build.json" },
|
|
15
14
|
{ "path": "../ui-themes/tsconfig.build.json" },
|
|
16
15
|
{ "path": "../emotion/tsconfig.build.json" },
|
|
17
16
|
{ "path": "../shared-types/tsconfig.build.json" },
|
|
@@ -20,8 +19,6 @@
|
|
|
20
19
|
{ "path": "../ui-i18n/tsconfig.build.json" },
|
|
21
20
|
{ "path": "../ui-prop-types/tsconfig.build.json" },
|
|
22
21
|
{ "path": "../ui-react-utils/tsconfig.build.json" },
|
|
23
|
-
{ "path": "../ui-test-locator/tsconfig.build.json" },
|
|
24
|
-
{ "path": "../ui-test-queries/tsconfig.build.json" },
|
|
25
22
|
{ "path": "../ui-testable/tsconfig.build.json" },
|
|
26
23
|
{ "path": "../ui-utils/tsconfig.build.json" }
|
|
27
24
|
]
|