| /** |
| * @license |
| * Copyright 2022 Google LLC |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| import '../test/test-setup'; |
| import './codemirror-element'; |
| import {assert, fixture, html} from '@open-wc/testing'; |
| import {CodeMirrorElement} from './codemirror-element'; |
| |
| suite('codemirror-element tests', () => { |
| let element: CodeMirrorElement; |
| |
| setup(async () => { |
| element = await fixture<CodeMirrorElement>( |
| html`<codemirror-element></codemirror-element>`, |
| ); |
| element.lineNum = 123; |
| element.prefs = { |
| match_brackets: true, |
| }; |
| await element.updateComplete; |
| }); |
| |
| test('render', () => { |
| assert.shadowDom.equal( |
| element, |
| /* HTML */ ` |
| <div id="wrapper"> |
| <div class="cm-editor ͼ1 ͼ1f ͼ1g ͼ1h ͼ1i ͼ2 ͼ4"> |
| <div aria-live="polite" class="cm-announced"></div> |
| <div class="cm-scroller" tabindex="-1"> |
| <div |
| aria-hidden="true" |
| class="cm-gutters cm-gutters-before" |
| style="min-height: 14px; position: sticky;" |
| > |
| <div class="cm-gutter cm-lineNumbers"> |
| <div |
| class="cm-gutterElement" |
| style="height: 0px; visibility: hidden; pointer-events: none;" |
| > |
| 9 |
| </div> |
| <div |
| class="cm-activeLineGutter cm-gutterElement" |
| style="height: 14px;" |
| > |
| 1 |
| </div> |
| </div> |
| <div class="cm-foldGutter cm-gutter"> |
| <div |
| class="cm-gutterElement" |
| style="height: 0px; visibility: hidden; pointer-events: none;" |
| > |
| <span title="Unfold line"> › </span> |
| </div> |
| <div |
| class="cm-activeLineGutter cm-gutterElement" |
| style="height: 14px;" |
| ></div> |
| </div> |
| </div> |
| <div |
| aria-multiline="true" |
| autocapitalize="off" |
| autocorrect="off" |
| class="cm-content" |
| contenteditable="true" |
| role="textbox" |
| spellcheck="false" |
| style="tab-size: 0;" |
| translate="no" |
| writingsuggestions="false" |
| > |
| <div class="cm-line"> |
| <br /> |
| </div> |
| </div> |
| <div |
| aria-hidden="true" |
| class="cm-cursorLayer cm-layer cm-layer-above" |
| style="z-index: 150; animation-duration: 1200ms;" |
| ></div> |
| <div |
| aria-hidden="true" |
| class="cm-layer cm-selectionLayer" |
| style="z-index: -2;" |
| ></div> |
| </div> |
| </div> |
| </div> |
| <div class="statusLine"> |
| <div class="cursorPosition"> |
| <span id="result"> Line: 1, Column: 1 </span> |
| </div> |
| </div> |
| `, |
| ); |
| }); |
| |
| async function waitForEditorInstance( |
| el: CodeMirrorElement, |
| attempts = 20, |
| delayMs = 50, |
| ) { |
| for (let i = 0; i < attempts && !el.editor; i++) { |
| await new Promise<void>(resolve => { |
| setTimeout((): void => resolve(), delayMs); |
| }); |
| } |
| return el.editor; |
| } |
| |
| function waitForContentChangeEvent(el: CodeMirrorElement) { |
| return new Promise<CustomEvent<{value: string}>>(resolve => { |
| el.addEventListener( |
| 'content-change', |
| (e: Event): void => { |
| resolve(e as CustomEvent<{value: string}>); |
| }, |
| {once: true}, |
| ); |
| }); |
| } |
| |
| test('dispatches content-change event when editor content changes', async () => { |
| const initialContent = 'first line\nsecond line\n'; |
| |
| const editorElement = await fixture<CodeMirrorElement>( |
| html` |
| <codemirror-element .fileContent=${initialContent}></codemirror-element> |
| `, |
| ); |
| |
| await editorElement.updateComplete; |
| |
| const editorView = await waitForEditorInstance(editorElement); |
| assert.isOk(editorView); |
| |
| const eventPromise = waitForContentChangeEvent(editorElement); |
| |
| void editorView.dispatch({ |
| changes: { |
| from: 0, |
| to: 0, |
| insert: 'X', |
| }, |
| }); |
| |
| const event = await eventPromise; |
| |
| assert.isString(event.detail.value); |
| assert.isTrue(event.detail.value.length > initialContent.length); |
| }); |
| |
| test('content-change preserves LF for LF-only inputs', async () => { |
| const editorElement = await fixture<CodeMirrorElement>( |
| html` |
| <codemirror-element .fileContent=${'a\nb\n'}></codemirror-element> |
| `, |
| ); |
| |
| await editorElement.updateComplete; |
| |
| const editorView = await waitForEditorInstance(editorElement); |
| assert.isOk(editorView); |
| |
| const eventPromise = waitForContentChangeEvent(editorElement); |
| |
| void editorView.dispatch({ |
| changes: { |
| from: 0, |
| to: 0, |
| insert: 'x', |
| }, |
| }); |
| |
| const event = await eventPromise; |
| |
| assert.isFalse(/\r\n/.test(event.detail.value)); |
| assert.isTrue(event.detail.value.endsWith('\n')); |
| }); |
| |
| test('content-change preserves CRLF for CRLF inputs', async () => { |
| const editorElement = await fixture<CodeMirrorElement>( |
| html` |
| <codemirror-element .fileContent=${'a\r\nb\r\n'}></codemirror-element> |
| `, |
| ); |
| |
| await editorElement.updateComplete; |
| |
| const editorView = await waitForEditorInstance(editorElement); |
| assert.isOk(editorView); |
| |
| const eventPromise = waitForContentChangeEvent(editorElement); |
| |
| void editorView.dispatch({ |
| changes: { |
| from: 0, |
| to: 0, |
| insert: 'x', |
| }, |
| }); |
| |
| const event = await eventPromise; |
| |
| assert.isString(event.detail.value); |
| assert.isTrue(/\r\n/.test(event.detail.value)); |
| assert.isTrue(event.detail.value.endsWith('\r\n')); |
| }); |
| }); |