hello world
'; await el.updateComplete; expect(el.getHTML()).to.equal('hello world
'); }); it('sanitizes hostile content set via value', async () => { const el = await editorFixture(); el.value = 'ok
click
'; await el.updateComplete; expect(el.getHTML()).to.not.include('script'); expect(el.getHTML()).to.not.include('onclick'); }); it('emits nile-input on document changes', async () => { const el = await editorFixture(); const view = el.pmView!; setTimeout(() => view.dispatch(view.state.tr.insertText('typed', 1))); const event = await oneEvent(el, 'nile-input'); expect(event.detail.value).to.equal('typed
'); expect(event.detail.content).to.equal('typed
'); expect(el.value).to.equal('typed
'); }); it('emits a debounced nile-change', async () => { const el = await editorFixture(); const view = el.pmView!; setTimeout(() => view.dispatch(view.state.tr.insertText('x', 1))); const event = await oneEvent(el, 'nile-change'); expect(event.detail.value).to.equal('x
'); }); it('applies commands via execCommand', async () => { const el = await editorFixture(); el.value = 'format me
'; await el.updateComplete; selectAll(el); expect(el.execCommand('bold')).to.equal(true); expect(el.getHTML()).to.equal('format me
'); expect(el.isCommandActive('bold')).to.equal(true); }); it('insertHTML inserts sanitized content at the selection', async () => { const el = await editorFixture(); el.insertHTML('safe'); expect(el.getHTML()).to.equal('safe
'); }); it('respects disabled and readonly', async () => { const el = await editorFixture( html`one
two
three
'; await el.updateComplete; expect(el.pmView!.state.doc.childCount).to.equal(1); }); it('executes toolbar commands dispatched as wysiwyg-command events', async () => { const el = await editorFixture(); el.value = 'toolbar
'; await el.updateComplete; selectAll(el); const toolbar = el.shadowRoot!.querySelector('nile-wysiwyg-toolbar')!; toolbar.dispatchEvent( new CustomEvent('wysiwyg-command', { detail: { name: 'italic' }, bubbles: true, composed: true, }) ); expect(el.getHTML()).to.equal('toolbar
'); }); it('participates in forms with name, required, and reset', async () => { const form = await fixturefilled
'); expect(el.checkValidity()).to.equal(true); expect(new FormData(form).get('notes')).to.equal('filled
'); form.reset(); await el.updateComplete; expect(el.getHTML()).to.equal(''); expect(el.checkValidity()).to.equal(false); }); it('uploads image files through uploadHandler with a placeholder', async () => { const el = await editorFixture(); let uploadedName = ''; el.uploadHandler = async file => { uploadedName = file.name; // Placeholder should be visible while the upload is pending. await new Promise(resolve => setTimeout(resolve, 30)); return { src: 'https://cdn.x.dev/uploaded.png', alt: 'uploaded' }; }; const file = new File(['x'], 'pic.png', { type: 'image/png' }); const pending = el.insertImageFile(file); await new Promise(resolve => setTimeout(resolve, 10)); expect( el.shadowRoot!.querySelector('.nile-wysiwyg__upload-placeholder'), 'placeholder while uploading' ).to.exist; await pending; expect(uploadedName).to.equal('pic.png'); expect(el.getHTML()).to.include('https://cdn.x.dev/uploaded.png'); expect(el.shadowRoot!.querySelector('.nile-wysiwyg__upload-placeholder')).to.not.exist; }); it('lets nile-image-upload-request listeners own the upload', async () => { const el = await editorFixture(); el.addEventListener('nile-image-upload-request', (e: Event) => { e.preventDefault(); (e as CustomEvent).detail.insert('https://custom.x.dev/own.png', 'mine'); }); await el.insertImageFile(new File(['x'], 'a.png', { type: 'image/png' })); expect(el.getHTML()).to.include('https://custom.x.dev/own.png'); expect(el.getHTML()).to.include('alt="mine"'); }); it('falls back to base64 embedding without a handler', async () => { const el = await editorFixture(); await el.insertImageFile(new File(['x'], 'a.png', { type: 'image/png' })); expect(el.getHTML()).to.include('data:image/png;base64'); }); it('emits nile-image-upload-error when the handler rejects', async () => { const el = await editorFixture(); el.uploadHandler = async () => { throw new Error('boom'); }; setTimeout(() => el.insertImageFile(new File(['x'], 'a.png', { type: 'image/png' }))); const event = await oneEvent(el, 'nile-image-upload-error'); expect(event.detail.error.message).to.equal('boom'); expect(el.getHTML()).to.equal(''); }); it('inserts tables from the table picker command event', async () => { const el = await editorFixture(); const toolbar = el.shadowRoot!.querySelector('nile-wysiwyg-toolbar')!; toolbar.dispatchEvent( new CustomEvent('wysiwyg-command', { detail: { name: 'insertTable', attrs: { rows: 2, cols: 3 } }, bubbles: true, composed: true, }) ); expect(el.getHTML()).to.include('x |
|
Label | $${key} |
$source.FirstName
'; await el.updateComplete; const chip = el.shadowRoot!.querySelector('.ProseMirror .nile-wysiwyg__mention') as HTMLElement; const lineHeight = parseFloat(getComputedStyle(chip).lineHeight) || 24; expect(chip.getBoundingClientRect().height).to.be.below(lineHeight * 1.6); }); }); describe('mention chip weight', () => { const chip = (key: string) => `$${key}`; const weightOf = (el: NileWysiwygEditor, key: string) => getComputedStyle( el.shadowRoot!.querySelector(`.ProseMirror [data-mention-key="${key}"]`)! ).fontWeight; it('keeps its default weight in plain text but follows bold, headings and explicit weights', async () => { const el = await editorFixture(html`Dear ${chip('first')}, ${chip('plain')}
` + `| ${chip('normalCell')} |