diff --git a/.eslintignore b/.eslintignore index 2ba1e3c9306..2ed46262c37 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,4 @@ # SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later -src/tests/ -*.d.ts +src/tests/fixtures diff --git a/src/markdownit/details.ts b/src/markdownit/details.ts index 2b0f1b2350d..48595c6a0ba 100644 --- a/src/markdownit/details.ts +++ b/src/markdownit/details.ts @@ -11,6 +11,13 @@ const DETAILS_START_REGEX = /^
\s*$/ const DETAILS_END_REGEX = /^<\/details>\s*$/ const SUMMARY_REGEX = /(?<=^).*(?=<\/summary>\s*$)/ +/** + * + * @param state + * @param startLine + * @param endLine + * @param silent + */ function parseDetails(state: StateBlock, startLine: number, endLine: number, silent: boolean) { // let autoClosedBlock = false let start = state.bMarks[startLine] + state.tShift[startLine] @@ -76,20 +83,20 @@ function parseDetails(state: StateBlock, startLine: number, endLine: number, sil state.parentType = 'reference' // This will prevent lazy continuations from ever going past our end marker - state.lineMax = nextLine; + state.lineMax = nextLine // Push tokens to the state let token = state.push('details_open', 'details', 1) token.block = true token.info = detailsSummary - token.map = [ startLine, nextLine ] + token.map = [startLine, nextLine] token = state.push('details_summary', 'summary', 1) token.block = false // Parse and push summary to preserve markup - let tokens: Token[] = [] + const tokens: Token[] = [] state.md.inline.parse(detailsSummary, state.md, state.env, tokens) for (const t of tokens) { token = state.push(t.type, t.tag, t.nesting) @@ -100,7 +107,7 @@ function parseDetails(state: StateBlock, startLine: number, endLine: number, sil token = state.push('details_summary', 'summary', -1) - state.md.block.tokenize(state, startLine + 2, nextLine); + state.md.block.tokenize(state, startLine + 2, nextLine) token = state.push('details_close', 'details', -1) token.block = true @@ -117,6 +124,6 @@ function parseDetails(state: StateBlock, startLine: number, endLine: number, sil */ export default function details(md: MarkdownIt) { md.block.ruler.before('fence', 'details', parseDetails, { - alt: [ 'paragraph', 'reference', 'blockquote', 'list' ], + alt: ['paragraph', 'reference', 'blockquote', 'list'], }) } diff --git a/src/tests/builders.js b/src/tests/builders.js index ce209b26866..5a52c045924 100644 --- a/src/tests/builders.js +++ b/src/tests/builders.js @@ -3,12 +3,16 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { expect } from '@jest/globals'; -import { Mark, Node } from '@tiptap/pm/model' +// eslint-disable-next-line n/no-extraneous-import +import { expect } from '@jest/globals' +import { Node } from '@tiptap/pm/model' import { builders } from 'prosemirror-test-builder' -import { createRichEditor } from '../EditorFactory' - +import { createRichEditor } from '../EditorFactory.js' +/** + * Get node builders from the default rich editor. + * @return {object} + */ export function getBuilders() { const editor = createRichEditor() return builders(editor.schema, { @@ -34,34 +38,24 @@ export const thead = getBuilders().thead /** * Create string representation of prosemirror / TipTap Node with attributes - * @param {Node} node - * @returns {string} + * @param {Node} node to serialize + * @return {string} */ function createDocumentString(node) { - /** - * Extract attributes of node or mark - * @param {Node|Mark} node - * @returns {string} - */ const extractAttributes = (node) => { const attrs = node.attrs || {} const attrString = Object.keys(attrs) .map((key) => { // null is the TipTap default so we ignore it (e.g. a value of `unknown` must be manually set by the application) - if (attrs[key] !== null) { - return key + '=' + (typeof attrs[key] === 'string' ? `"${attrs[key]}"` : attrs[key]) - } + return (attrs[key] === null) + ? undefined + : key + '=' + (typeof attrs[key] === 'string' ? `"${attrs[key]}"` : attrs[key]) }) .filter(v => !!v) .join(',') return attrString ? `<${attrString}>` : '' } - /** - * Create string representation of a single Node - * @param {Node} node - * @returns {string} - */ const stringifyNode = (node) => { const name = node.type.name if (name === 'text') return '"' + node.text.replace('"', '\\"').replace('\n', '\\n') + '"' @@ -83,9 +77,9 @@ function createDocumentString(node) { * @example * const editor = createRichEditor() * expectDocument(editor.state.doc, table( - * tr( - * td('foo') - * ) + * tr( + * td('foo') + * ) * )) */ export function expectDocument(subject, expected) { diff --git a/src/tests/extensions/Markdown.spec.js b/src/tests/extensions/Markdown.spec.js index beb42351792..8dc6c608e1f 100644 --- a/src/tests/extensions/Markdown.spec.js +++ b/src/tests/extensions/Markdown.spec.js @@ -5,13 +5,13 @@ import { Markdown } from './../../extensions/index.js' import { createMarkdownSerializer } from './../../extensions/Markdown.js' -import CodeBlock from '@tiptap/extension-code-block' -import Blockquote from '@tiptap/extension-blockquote' +import { CodeBlock } from '@tiptap/extension-code-block' +import { Blockquote } from '@tiptap/extension-blockquote' import Image from './../../nodes/Image.js' import ImageInline from './../../nodes/ImageInline.js' import TaskList from './../../nodes/TaskList.js' import TaskItem from './../../nodes/TaskItem.js' -import { Italic, Strong, Underline, Link} from './../../marks/index.js' +import { Italic, Strong, Underline, Link } from './../../marks/index.js' import TiptapImage from '@tiptap/extension-image' import { getExtensionField } from '@tiptap/core' import { __serializeForClipboard as serializeForClipboard } from '@tiptap/pm/view' @@ -114,7 +114,7 @@ describe('Markdown extension integrated in the editor', () => { it('copies address from blockquote to markdown', () => { const editor = createCustomEditor({ - content: '

Hermannsreute 44A

', + content: '

Hermannsreute 44A

', extensions: [Markdown, Blockquote], }) const text = copyEditorContent(editor) @@ -123,7 +123,7 @@ describe('Markdown extension integrated in the editor', () => { it('copy version number without escape character', () => { const editor = createCustomEditor({ - content: '

Hello

28.0.4

', + content: '

Hello

28.0.4

', extensions: [Markdown], }) const text = copyEditorContent(editor) @@ -132,7 +132,7 @@ describe('Markdown extension integrated in the editor', () => { it('strips bold, italic, and other marks from paragraph', () => { const editor = createCustomEditor({ - content: '

Hello

lonely world

', + content: '

Hello

lonely world

', extensions: [Markdown, Italic, Strong, Underline], }) const text = copyEditorContent(editor) @@ -141,7 +141,7 @@ describe('Markdown extension integrated in the editor', () => { it('strips href and link formatting from email address', () => { const editor = createCustomEditor({ - content: '

Hello

example@example.com

', + content: '

Hello

example@example.com

', extensions: [Markdown, Link], }) const text = copyEditorContent(editor) @@ -150,7 +150,7 @@ describe('Markdown extension integrated in the editor', () => { }) -function copyEditorContent(editor) { +const copyEditorContent = (editor) => { editor.commands.selectAll() const slice = editor.state.selection.content() const { text } = serializeForClipboard(editor.view, slice) diff --git a/src/tests/helpers.js b/src/tests/helpers.js index 53a44ac18fb..370f4abb879 100644 --- a/src/tests/helpers.js +++ b/src/tests/helpers.js @@ -3,16 +3,22 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { createMarkdownSerializer } from '../extensions/Markdown' +import { createMarkdownSerializer } from '../extensions/Markdown.js' import { Editor } from '@tiptap/core' -import Document from '@tiptap/extension-document' -import Paragraph from '../nodes/Paragraph' -import Text from '@tiptap/extension-text' +import { Document } from '@tiptap/extension-document' +import { Text } from '@tiptap/extension-text' +import Paragraph from '../nodes/Paragraph.js' -import { createRichEditor } from '../EditorFactory' -import markdownit from '../markdownit' +import { createRichEditor } from '../EditorFactory.js' +import markdownit from '../markdownit/index.js' +/** + * + * @param {object} options for the editor + * @param {string} options.content Content for the editor. + * @param {Array} options.extensions Tip tap extensions + */ export function createCustomEditor({ content, extensions }) { return new Editor({ content, @@ -21,15 +27,15 @@ export function createCustomEditor({ content, extensions }) { Paragraph, Text, ...extensions, - ] + ], }) } /** * Ease markdown through TipTap editor and return serialized markdown * - * @param {string} markdown - * @returns {string} + * @param {string} markdown to process + * @return {string} */ export function markdownThroughEditor(markdown) { const tiptap = createRichEditor() @@ -41,8 +47,8 @@ export function markdownThroughEditor(markdown) { /** * Ease HTML as input through the Editor and return the serialized markdown * - * @param {string} html - * @returns {string} + * @param {string} html to process + * @return {string} */ export function markdownThroughEditorHtml(html) { const tiptap = createRichEditor() @@ -54,8 +60,8 @@ export function markdownThroughEditorHtml(html) { /** * Paste HTML into the Editor and return the serialized markdown * - * @param {string} html - * @returns {string} + * @param {string} html to paste + * @return {string} */ export function markdownFromPaste(html) { const tiptap = createRichEditor() diff --git a/src/tests/helpers/base64.spec.js b/src/tests/helpers/base64.spec.js index fbdf3bc41df..424ae4d7a6c 100644 --- a/src/tests/helpers/base64.spec.js +++ b/src/tests/helpers/base64.spec.js @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { encodeArrayBuffer, decodeArrayBuffer } from '../../helpers/base64' +import { encodeArrayBuffer, decodeArrayBuffer } from '../../helpers/base64.ts' describe('encoding ArrayBuffer content', () => { test('empty array buffer is empty base64 string', () => { @@ -32,7 +32,7 @@ describe('decoding base64 to array buffer', () => { const encoded = 'AAAA' const buffer = decodeArrayBuffer(encoded) expect(buffer.byteLength).toBe(3) - const view = new Uint8Array(buffer); + const view = new Uint8Array(buffer) expect(view).toEqual(new Uint8Array([0, 0, 0])) }) }) diff --git a/src/tests/helpers/links.spec.js b/src/tests/helpers/links.spec.js index e31c4740e6d..79774fc00e7 100644 --- a/src/tests/helpers/links.spec.js +++ b/src/tests/helpers/links.spec.js @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { domHref, parseHref } from '../../helpers/links' +import { domHref, parseHref } from '../../helpers/links.js' import { loadState } from '@nextcloud/initial-state' global.OCA = { @@ -13,7 +13,7 @@ global.OCA = { } global.OC = { - config: {modRewriteWorking: true}, + config: { modRewriteWorking: true }, } global._oc_webroot = '' @@ -30,7 +30,7 @@ describe('Preparing href attributes for the DOM', () => { }) test('leave undefined hrefs alone', () => { - expect(domHref({attrs: {}})).toBe(undefined) + expect(domHref({ attrs: {} })).toBe(undefined) }) test('full url', () => { @@ -88,8 +88,8 @@ describe('Extracting short urls from the DOM', () => { describe('Inserting hrefs into the dom and extracting them again', () => { - function insertAndExtract(attrs) { - const node = {attrs} + const insertAndExtract = (attrs) => { + const node = { attrs } const dom = { getAttribute() { return domHref(node) @@ -99,7 +99,7 @@ describe('Inserting hrefs into the dom and extracting them again', () => { } test('leave empty hrefs alone', () => { - expect(insertAndExtract({href: ''})).toBe('') + expect(insertAndExtract({ href: '' })).toBe('') }) test('leave undefined hrefs alone', () => { @@ -107,27 +107,27 @@ describe('Inserting hrefs into the dom and extracting them again', () => { }) test('old relative link format (from file picker) is rewritten', () => { - expect(insertAndExtract({href: 'otherfile?fileId=123'})) + expect(insertAndExtract({ href: 'otherfile?fileId=123' })) .toBe('http://localhost/f/123') }) test('old relative link format with ../ (from file picker) is rewritten', () => { - expect(insertAndExtract({href: '../otherfile?fileId=123'})) + expect(insertAndExtract({ href: '../otherfile?fileId=123' })) .toBe('http://localhost/f/123') }) test('old absolute link format (from file picker) is rewritten', () => { - expect(insertAndExtract({href: '/otherfile?fileId=123'})) + expect(insertAndExtract({ href: '/otherfile?fileId=123' })) .toBe('http://localhost/f/123') }) test('default full URL link format is unchanged', () => { - expect(insertAndExtract({href: 'http://localhost/f/123'})) + expect(insertAndExtract({ href: 'http://localhost/f/123' })) .toBe('http://localhost/f/123') }) test('absolute link to collectives page is unchanged', () => { - expect(insertAndExtract({href: '/apps/collectives/page?fileId=123'})) + expect(insertAndExtract({ href: '/apps/collectives/page?fileId=123' })) .toBe('/apps/collectives/page?fileId=123') }) diff --git a/src/tests/markdown.spec.js b/src/tests/markdown.spec.js index f15cec281c7..204876c9dae 100644 --- a/src/tests/markdown.spec.js +++ b/src/tests/markdown.spec.js @@ -3,15 +3,15 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import markdownit from './../markdownit' -import { typesAvailable } from './../markdownit/callouts' +import markdownit from './../markdownit/index.js' +import { typesAvailable } from './../markdownit/callouts.js' import { markdownThroughEditor, markdownThroughEditorHtml, - markdownFromPaste + markdownFromPaste, } from './helpers.js' -import { createMarkdownSerializer } from "../extensions/Markdown"; -import { createRichEditor } from "../EditorFactory"; +import { createMarkdownSerializer } from '../extensions/Markdown.js' +import { createRichEditor } from '../EditorFactory.js' /* * This file is for various markdown tests, mainly testing if input and output stays the same. @@ -91,11 +91,11 @@ describe('Markdown though editor', () => { }) test('escaping', () => { - const test = '(Asdf [asdf asdf](asdf asdf) asdf asdf asdf asdf asdf asdf asdf asdf asdf)\n' + - '\n' + - '* [asdf asdf asdf/asdf](Asdf Asdf)\n' + - '* asdf asdf asdf [a--f asdf asdf](a--f Asdf Asdf)\n' + - '* [Asdf asdf asdf asdf asdf asdf](Asdf asdf)' + const test = '(Asdf [asdf asdf](asdf asdf) asdf asdf asdf asdf asdf asdf asdf asdf asdf)\n' + + '\n' + + '* [asdf asdf asdf/asdf](Asdf Asdf)\n' + + '* asdf asdf asdf [a--f asdf asdf](a--f Asdf Asdf)\n' + + '* [Asdf asdf asdf asdf asdf asdf](Asdf asdf)' expect(markdownThroughEditor(test)).toBe(test) expect(markdownThroughEditor('This is a [test] for escaping')).toBe('This is a [test] for escaping') expect(markdownThroughEditor('This is a [test for escaping')).toBe('This is a [test for escaping') @@ -164,17 +164,17 @@ describe('Markdown serializer from html', () => { test('callouts', () => { typesAvailable.forEach(type => { expect(markdownThroughEditorHtml( - `

!${type}!

just do it

` + `

!${type}!

just do it

`, )).toBe(`::: ${type}\n!${type}!\n\njust do it\n\n:::`) expect(markdownThroughEditorHtml( - `

!${type}!

` + `

!${type}!

`, )).toBe(`::: ${type}\n!${type}!\n\n:::`) }) }) test('callouts with handbook classes', () => { expect(markdownThroughEditorHtml( - `

!warning!

` - )).toBe(`::: warn\n!warning!\n\n:::`) + '

!warning!

', + )).toBe('::: warn\n!warning!\n\n:::') }) test('table', () => { @@ -211,7 +211,7 @@ describe('Markdown serializer from html', () => { describe('Trailing nodes', () => { test('No extra transaction is added after loading', () => { - const source = "# My heading\n\n* test\n* test2" + const source = '# My heading\n\n* test\n* test2' const tiptap = createRichEditor() tiptap.commands.setContent(markdownit.render(source)) diff --git a/src/tests/markdownit/bulletList.spec.js b/src/tests/markdownit/bulletList.spec.js index 4a56e1ce650..4c99373d865 100644 --- a/src/tests/markdownit/bulletList.spec.js +++ b/src/tests/markdownit/bulletList.spec.js @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import markdownit from '../../markdownit' -import stripIndent from './stripIndent' +import markdownit from '../../markdownit/index.js' +import stripIndent from './stripIndent.js' describe('markdownit', () => { @@ -15,7 +15,7 @@ describe('markdownit', () => { ` + `, )) }) }) @@ -28,7 +28,7 @@ describe('markdownit', () => { ` + `, )) }) diff --git a/src/tests/markdownit/callouts.spec.js b/src/tests/markdownit/callouts.spec.js index ce687f92d00..eb4a6ddf669 100644 --- a/src/tests/markdownit/callouts.spec.js +++ b/src/tests/markdownit/callouts.spec.js @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import markdownit from '../../markdownit' -import { typesAvailable } from '../../markdownit/callouts' +import markdownit from '../../markdownit/index.js' +import { typesAvailable } from '../../markdownit/callouts.js' import stripIndent from './stripIndent.js' describe('callouts', () => { @@ -14,9 +14,8 @@ describe('callouts', () => { expect(stripIndent(rendered)).toBe(stripIndent( `

Hey there!

-
` + `, )) }) }) }) - diff --git a/src/tests/markdownit/commonmark.spec.js b/src/tests/markdownit/commonmark.spec.js index 90760b0ffb6..18f9f54cea9 100644 --- a/src/tests/markdownit/commonmark.spec.js +++ b/src/tests/markdownit/commonmark.spec.js @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import spec from "../fixtures/spec" -import markdownit from '../../markdownit' +import spec from '../fixtures/spec.js' +import markdownit from '../../markdownit/index.js' describe('Commonmark', () => { const skippedMarkdownTests = [ @@ -14,7 +14,7 @@ describe('Commonmark', () => { 21, 31, 201, 344, 474, 475, 476, 490, 493, 523, 535, 642, 643, // contain comments 309, 308, - ]; + ] const normalize = (str) => { // https://github.com/markdown-it/markdown-it/blob/df4607f1d4d4be7fdc32e71c04109aea8cc373fa/test/commonmark.js#L10 @@ -26,12 +26,12 @@ describe('Commonmark', () => { // special treatment because we use markdown-it-image-figures const figureImageMarkdownTests = [ - 516, 519, 530, 571, 572, 573, 574, 575, 576, 577, 579, 580, 581, 582, 583, 584, 585, 587, 588, 590 + 516, 519, 530, 571, 572, 573, 574, 575, 576, 577, 579, 580, 581, 582, 583, 584, 585, 587, 588, 590, ] spec.forEach((entry) => { // We do not support HTML - if (entry.section === 'HTML blocks' || entry.section === 'Raw HTML') return; + if (entry.section === 'HTML blocks' || entry.section === 'Raw HTML') return if (skippedMarkdownTests.indexOf(entry.example) !== -1) { return diff --git a/src/tests/markdownit/details.spec.js b/src/tests/markdownit/details.spec.js index 31c46e4287f..e5755f7efec 100644 --- a/src/tests/markdownit/details.spec.js +++ b/src/tests/markdownit/details.spec.js @@ -3,93 +3,92 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import markdownit from '../../markdownit' +import markdownit from '../../markdownit/index.js' import stripIndent from './stripIndent.js' describe('Details extension', () => { - it(`renders`, () => { - const rendered = markdownit.render(`
\nsummary\ncontent\n
`) + it('renders', () => { + const rendered = markdownit.render('
\nsummary\ncontent\n
') expect(stripIndent(rendered)).toBe( - `
summary

content

` + '
summary

content

', ) }) - it(`renders with empty summary`, () => { - const rendered = markdownit.render(`
\n\ncontent\n
`) + it('renders with empty summary', () => { + const rendered = markdownit.render('
\n\ncontent\n
') expect(stripIndent(rendered)).toBe( - `

content

` + '

content

', ) }) - it(`renders with empty content`, () => { - const rendered = markdownit.render(`
\n\n
`) + it('renders with empty content', () => { + const rendered = markdownit.render('
\n\n
') expect(stripIndent(rendered)).toBe( - `
` + '
', ) }) - it(`renders with spaces`, () => { - const rendered = markdownit.render(`
\n summary \n content \n
`) + it('renders with spaces', () => { + const rendered = markdownit.render('
\n summary \n content \n
') expect(stripIndent(rendered)).toBe( - `
summary

content

` + '
summary

content

', ) }) - it(`renders with marks in summary`, () => { - const rendered = markdownit.render(`
\n**summary**\ncontent\n
`) + it('renders with marks in summary', () => { + const rendered = markdownit.render('
\n**summary**\ncontent\n
') expect(stripIndent(rendered)).toBe( - `
summary

content

` + '
summary

content

', ) }) - it(`renders with marks in content`, () => { - const rendered = markdownit.render(`
\nsummary\n**content**\n
`) + it('renders with marks in content', () => { + const rendered = markdownit.render('
\nsummary\n**content**\n
') expect(stripIndent(rendered)).toBe( - `
summary

content

` + '
summary

content

', ) }) - it(`renders with block elements in content`, () => { - const rendered = markdownit.render(`
\nsummary\nparagraph\n- one\n- two\n
`) + it('renders with block elements in content', () => { + const rendered = markdownit.render('
\nsummary\nparagraph\n- one\n- two\n
') expect(stripIndent(rendered)).toBe( - `
summary

paragraph

  • one
  • two
` + '
summary

paragraph

  • one
  • two
', ) }) - it(`renders nested details`, () => { - const rendered = markdownit.render(`
\nsummary\n
\nnested summary\nnested content\n
\ncontent\n
`) + it('renders nested details', () => { + const rendered = markdownit.render('
\nsummary\n
\nnested summary\nnested content\n
\ncontent\n
') expect(stripIndent(rendered)).toBe( - `
summary
nested summary

nested content

content

` + '
summary
nested summary

nested content

content

', ) }) - it(`does not render with missing linebreak after details open`, () => { - const rendered = markdownit.render(`
summary\ncontent\n
`) + it('does not render with missing linebreak after details open', () => { + const rendered = markdownit.render('
summary\ncontent\n
') expect(stripIndent(rendered)).toBe( - `

<details><summary>summary</summary>content</details>

` + '

<details><summary>summary</summary>content</details>

', ) }) - it(`does not render with missing linebreak after summary`, () => { - const rendered = markdownit.render(`
\nsummarycontent\n
`) + it('does not render with missing linebreak after summary', () => { + const rendered = markdownit.render('
\nsummarycontent\n
') expect(stripIndent(rendered)).toBe( - `

<details><summary>summary</summary>content</details>

` + '

<details><summary>summary</summary>content</details>

', ) }) - it(`does not render with missing linebreak before details close`, () => { - const rendered = markdownit.render(`
\nsummary\ncontent
`) + it('does not render with missing linebreak before details close', () => { + const rendered = markdownit.render('
\nsummary\ncontent
') expect(stripIndent(rendered)).toBe( - `

<details><summary>summary</summary>content</details>

` + '

<details><summary>summary</summary>content</details>

', ) }) - it(`does not render without summary`, () => { - const rendered = markdownit.render(`
\ncontent\n
`) + it('does not render without summary', () => { + const rendered = markdownit.render('
\ncontent\n
') expect(stripIndent(rendered)).toBe( - `

<details>content</details>

` + '

<details>content</details>

', ) }) - it(`does not render with missing closing tag`, () => { - const rendered = markdownit.render(`
\nsummary\ncontent`) + it('does not render with missing closing tag', () => { + const rendered = markdownit.render('
\nsummary\ncontent') expect(stripIndent(rendered)).toBe( - `

<details><summary>summary</summary>content

` + '

<details><summary>summary</summary>content

', ) }) - it(`does not render with just summary`, () => { - const rendered = markdownit.render(`summary`) + it('does not render with just summary', () => { + const rendered = markdownit.render('summary') expect(stripIndent(rendered)).toBe( - `

<summary>summary</summary>

` + '

<summary>summary</summary>

', ) }) }) - diff --git a/src/tests/markdownit/imageFigures.spec.js b/src/tests/markdownit/imageFigures.spec.js index 633d9727cf6..ac0ce9abd1c 100644 --- a/src/tests/markdownit/imageFigures.spec.js +++ b/src/tests/markdownit/imageFigures.spec.js @@ -3,13 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import markdownit from '../../markdownit' +import markdownit from '../../markdownit/index.js' describe('image figures extension', () => { it('renders images as figures', () => { expect(markdownit.render('[![moon](moon.jpg)](/uri)\n')) - .toBe('
\"moon\"
\n') + .toBe('
moon
\n') }) }) diff --git a/src/tests/markdownit/mentions.spec.js b/src/tests/markdownit/mentions.spec.js index 66bb5128a05..cf556555405 100644 --- a/src/tests/markdownit/mentions.spec.js +++ b/src/tests/markdownit/mentions.spec.js @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import markdownit from '../../markdownit' +import markdownit from '../../markdownit/index.js' import stripIndent from './stripIndent.js' describe('markdownit', () => { @@ -11,7 +11,7 @@ describe('markdownit', () => { it('renders mentions of users with escaped whitespace', () => { const rendered = markdownit.render('@[whitespace user](mention://user/whitespace%20user)') expect(stripIndent(rendered)).toBe(stripIndent(` -

whitespace user

` +

whitespace user

`, )) }) diff --git a/src/tests/markdownit/preview.spec.js b/src/tests/markdownit/preview.spec.js index cdec9271565..bd7e1a18a3b 100644 --- a/src/tests/markdownit/preview.spec.js +++ b/src/tests/markdownit/preview.spec.js @@ -3,43 +3,42 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import markdownit from '../../markdownit' -import stripIndent from './stripIndent.js' +import markdownit from '../../markdownit/index.js' describe('Preview extension', () => { const link = { - md: `[link](https://nextcloud.com)`, - html: `link`, + md: '[link](https://nextcloud.com)', + html: 'link', } const preview = { - md: `[link](https://nextcloud.com (preview))`, - html: `link`, + md: '[link](https://nextcloud.com (preview))', + html: 'link', } it('wraps', () => { expect(markdownit.render('[link](https://nextcloud.com)')) .toBe( - `

link

\n` + '

link

\n', ) }) - it(`unwraps preview from paragraph`, () => { + it('unwraps preview from paragraph', () => { const rendered = markdownit.render(preview.md) expect(rendered).toBe(preview.html) }) - it(`leaves non-preview links alone`, () => { + it('leaves non-preview links alone', () => { const rendered = markdownit.render(link.md) expect(rendered).toBe( - `

${link.html}

\n` + `

${link.html}

\n`, ) }) - it(`leaves two previews in one paragraph`, () => { + it('leaves two previews in one paragraph', () => { const rendered = markdownit.render(`${preview.md}\n${preview.md}`) expect(rendered).toBe( - `

${preview.html}\n${preview.html}

\n` + `

${preview.html}\n${preview.html}

\n`, ) }) diff --git a/src/tests/markdownit/stripIndent.js b/src/tests/markdownit/stripIndent.js index 305defe7b15..85cffdc47d9 100644 --- a/src/tests/markdownit/stripIndent.js +++ b/src/tests/markdownit/stripIndent.js @@ -3,10 +3,14 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +/** + * Strip the indent to allow indenting expected results + * @param {string} content - string to strip indent from. + */ export default function stripIndent(content) { return content - .replace(/\n/g, "") - .replace(/[\t ]+\[\t ]+\<") - .replace(/\>[\t ]+$/g, ">") + .replace(/\n/g, '') + .replace(/[\t ]+[\t ]+<') + .replace(/>[\t ]+$/g, '>') } diff --git a/src/tests/markdownit/taskLists.spec.js b/src/tests/markdownit/taskLists.spec.js index 4394d70e32d..1e5c45b9cf5 100644 --- a/src/tests/markdownit/taskLists.spec.js +++ b/src/tests/markdownit/taskLists.spec.js @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import markdownit from '../../markdownit' -import stripIndent from './stripIndent' +import markdownit from '../../markdownit/index.js' +import stripIndent from './stripIndent.js' describe('task list extension', () => { @@ -16,9 +16,8 @@ describe('task list extension', () => {
  • not a task
  • -
` - )) + `, + )) }) }) - diff --git a/src/tests/marks/Underline.spec.js b/src/tests/marks/Underline.spec.js index 3332ae81f7f..3aa45f59e49 100644 --- a/src/tests/marks/Underline.spec.js +++ b/src/tests/marks/Underline.spec.js @@ -3,9 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import Underline from './../../marks/Underline'; +import Underline from './../../marks/Underline.js' import { getExtensionField } from '@tiptap/core' -import { createCustomEditor } from '../helpers' +import { createCustomEditor } from '../helpers.js' describe('Underline extension unit', () => { it('exposes toMarkdown function', () => { diff --git a/src/tests/nodes/Preview.spec.js b/src/tests/nodes/Preview.spec.js index 4e8e82dd8d2..48817a5a5ab 100644 --- a/src/tests/nodes/Preview.spec.js +++ b/src/tests/nodes/Preview.spec.js @@ -3,12 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import Preview from './../../nodes/Preview' -import Markdown from './../../extensions/Markdown' -import Link from './../../marks/Link' +import Preview from './../../nodes/Preview.js' +import Markdown from './../../extensions/Markdown.js' +import Link from './../../marks/Link.js' import { getExtensionField } from '@tiptap/core' -import { createCustomEditor, markdownThroughEditor, markdownThroughEditorHtml } from '../helpers' -import markdownit from '../../markdownit/index.js' +import { createCustomEditor, markdownThroughEditor, markdownThroughEditorHtml } from '../helpers.js' describe('Preview extension', () => { it('exposes toMarkdown function', () => { @@ -23,19 +22,19 @@ describe('Preview extension', () => { }) it('markdown syntax is preserved through editor', () => { - const markdown = `[link](https://nextcloud.com (preview))` + const markdown = '[link](https://nextcloud.com (preview))' expect(markdownThroughEditor(markdown)).toBe(markdown) }) it('serializes HTML to markdown', () => { - const markdown = `[link](https://nextcloud.com (preview))` - const link = `link` + const markdown = '[link](https://nextcloud.com (preview))' + const link = 'link' expect(markdownThroughEditorHtml(link)) .toBe(markdown) }) it('detects links', () => { - const link = `link` + const link = 'link' const editor = createEditorWithPreview() editor.commands.setContent(`${link}

hello>

`) const node = editor.state.doc.content.firstChild @@ -46,8 +45,6 @@ describe('Preview extension', () => { }) -function createEditorWithPreview() { - return createCustomEditor({ - extensions: [Markdown, Preview, Link] - }) -} +const createEditorWithPreview = () => createCustomEditor({ + extensions: [Markdown, Preview, Link], +}) diff --git a/src/tests/nodes/Table.spec.js b/src/tests/nodes/Table.spec.js index 06dabbea0ef..9736e6ccf6a 100644 --- a/src/tests/nodes/Table.spec.js +++ b/src/tests/nodes/Table.spec.js @@ -3,11 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { createRichEditor } from '../../EditorFactory' -import { createMarkdownSerializer } from '../../extensions/Markdown' -import { builders } from 'prosemirror-test-builder' +import { createRichEditor } from '../../EditorFactory.js' +import { createMarkdownSerializer } from '../../extensions/Markdown.js' -import markdownit from '../../markdownit' +import markdownit from '../../markdownit/index.js' import input from '../fixtures/tables/basic/table.md' import output from '../fixtures/tables/basic/table.html' @@ -15,7 +14,7 @@ import otherStructure from '../fixtures/tables/basic/table.structure.html' import handbook from '../fixtures/tables/handbook/handbook.html' import handbookOut from '../fixtures/tables/handbook/handbook.out.html' -import { br, table, td, th, thead, tr, expectDocument } from '../builders' +import { br, table, td, th, thead, tr, expectDocument } from '../builders.js' describe('Table', () => { it('Markdown-IT renders tables', () => { @@ -31,14 +30,14 @@ describe('Table', () => { thead( th({ textAlign: 'center' }, 'heading'), th({ textAlign: 'right' }, 'heading 2'), - th('heading 3') + th('heading 3'), ), tr( td({ textAlign: 'center' }, 'center'), td({ textAlign: 'right' }, 'right'), - td('left cell ', br({ syntax: 'html' }), 'with line break') - ) - ) + td('left cell ', br({ syntax: 'html' }), 'with line break'), + ), + ), ) }) @@ -50,14 +49,14 @@ describe('Table', () => { thead( th({ textAlign: 'center' }, 'heading'), th({ textAlign: 'right' }, 'heading 2'), - th('heading 3') + th('heading 3'), ), tr( td({ textAlign: 'center' }, 'center'), td({ textAlign: 'right' }, 'right'), - td('left cell ', br({ syntax: ' ' }), 'with line break') - ) - ) + td('left cell ', br({ syntax: ' ' }), 'with line break'), + ), + ), ) }) @@ -74,12 +73,12 @@ describe('Table', () => { }) }) -function editorWithContent(content) { +const editorWithContent = (content) => { const editor = createRichEditor() editor.commands.setContent(content) return editor } -function formatHTML(html) { +const formatHTML = (html) => { return html.replaceAll('><', '>\n<').replace(/\n$/, '') } diff --git a/src/tests/nodes/TaskItem.spec.js b/src/tests/nodes/TaskItem.spec.js index 05e572cd19c..2f7cc84e8a1 100644 --- a/src/tests/nodes/TaskItem.spec.js +++ b/src/tests/nodes/TaskItem.spec.js @@ -3,11 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import TaskList from './../../nodes/TaskList' -import TaskItem from './../../nodes/TaskItem' -import Markdown from './../../extensions/Markdown' +import TaskList from './../../nodes/TaskList.js' +import TaskItem from './../../nodes/TaskItem.js' +import Markdown from './../../extensions/Markdown.js' import { getExtensionField } from '@tiptap/core' -import { createCustomEditor, markdownThroughEditor, markdownThroughEditorHtml } from '../helpers' +import { createCustomEditor, markdownThroughEditor, markdownThroughEditorHtml } from '../helpers.js' describe('TaskItem extension', () => { it('exposes toMarkdown function', () => { @@ -17,7 +17,7 @@ describe('TaskItem extension', () => { it('exposes the toMarkdown function in the prosemirror schema', () => { const editor = createCustomEditor({ - extensions: [Markdown, TaskList, TaskItem] + extensions: [Markdown, TaskList, TaskItem], }) const taskItem = editor.schema.nodes.taskItem expect(taskItem.spec.toMarkdown).toBeDefined() @@ -35,13 +35,13 @@ describe('TaskItem extension', () => { expect(markdownThroughEditor('- [ ] [asd](sdf)')).toBe('- [ ] [asd](sdf)') expect(markdownThroughEditor('- [x] [asd](sdf)')).toBe('- [x] [asd](sdf)') expect(markdownThroughEditor('- [ ] foo\n- [x] bar')).toBe('- [ ] foo\n- [x] bar') - expect(markdownThroughEditor('- [x] foo\n' + - ' - [ ] bar\n' + - ' - [x] baz\n' + - '- [ ] bim')).toBe('- [x] foo\n' + - ' - [ ] bar\n' + - ' - [x] baz\n' + - '- [ ] bim') + expect(markdownThroughEditor('- [x] foo\n' + + ' - [ ] bar\n' + + ' - [x] baz\n' + + '- [ ] bim')).toBe('- [x] foo\n' + + ' - [ ] bar\n' + + ' - [x] baz\n' + + '- [ ] bim') expect(markdownThroughEditor('- [X] asd')).toBe('- [x] asd') expect(markdownThroughEditor('- [X] asd')).toBe('- [x] asd') }) diff --git a/src/tests/plaintext.spec.js b/src/tests/plaintext.spec.js index 8fd957236fc..5a9cb9e98f4 100644 --- a/src/tests/plaintext.spec.js +++ b/src/tests/plaintext.spec.js @@ -3,86 +3,86 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { createPlainEditor, serializePlainText } from './../EditorFactory'; -import spec from "./fixtures/spec" -import xssFuzzVectors from './fixtures/xssFuzzVectors'; +import { createPlainEditor, serializePlainText } from './../EditorFactory.js' +import spec from './fixtures/spec.js' +import xssFuzzVectors from './fixtures/xssFuzzVectors.js' const escapeHTML = (s) => { - return s.toString() - .split('&').join('&') - .split('<').join('<') - .split('>').join('>') - .split('"').join('"') - .split('\'').join(''') + return s.toString() + .split('&').join('&') + .split('<').join('<') + .split('>').join('>') + .split('"').join('"') + .split('\'').join(''') } const plaintextThroughEditor = (markdown) => { - const content = '
' + escapeHTML(markdown) + '
' - const tiptap = createPlainEditor() - tiptap.commands.setContent(content) - return serializePlainText(tiptap.state.doc) || 'failed' + const content = '
' + escapeHTML(markdown) + '
' + const tiptap = createPlainEditor() + tiptap.commands.setContent(content) + return serializePlainText(tiptap.state.doc) || 'failed' } describe('commonmark as plaintext', () => { - // FIXME: Those two tests currently fail as trailing whitespace seems to be stripped, - // if it occurs in the first line which is empty otherwise. - const skippedMarkdownTests = [ - 97, 117 - ]; + // FIXME: Those two tests currently fail as trailing whitespace seems to be stripped, + // if it occurs in the first line which is empty otherwise. + const skippedMarkdownTests = [ + 97, 117, + ] - spec.forEach((entry) => { - if (skippedMarkdownTests.indexOf(entry.example) !== -1) { - return - } - test('commonmark ' + entry.example, () => { - expect(plaintextThroughEditor(entry.markdown)).toBe(entry.markdown) - }) - }) + spec.forEach((entry) => { + if (skippedMarkdownTests.indexOf(entry.example) !== -1) { + return + } + test('commonmark ' + entry.example, () => { + expect(plaintextThroughEditor(entry.markdown)).toBe(entry.markdown) + }) + }) }) describe('markdown as plaintext', () => { - test('headlines', () => { - expect(plaintextThroughEditor('# Test')).toBe('# Test') - expect(plaintextThroughEditor('## Test')).toBe('## Test') - expect(plaintextThroughEditor('### Test')).toBe('### Test') - expect(plaintextThroughEditor('#### Test')).toBe('#### Test') - expect(plaintextThroughEditor('##### Test')).toBe('##### Test') - }) - test('inline format', () => { - expect(plaintextThroughEditor('**Test**')).toBe('**Test**') - expect(plaintextThroughEditor('__Test__')).toBe('__Test__') - expect(plaintextThroughEditor('_Test_')).toBe('_Test_') - expect(plaintextThroughEditor('~~Test~~')).toBe('~~Test~~') - }) - test('ul', () => { - expect(plaintextThroughEditor('- foo\n- bar')).toBe('- foo\n- bar') - expect(plaintextThroughEditor('- foo\n\n- bar')).toBe('- foo\n\n- bar') - expect(plaintextThroughEditor('- foo\n\n\n- bar')).toBe('- foo\n\n\n- bar') - }) - test('ol', () => { - expect(plaintextThroughEditor('1. foo\n2. bar')).toBe('1. foo\n2. bar') - }) - test('paragraph', () => { - expect(plaintextThroughEditor('foo\nbar\n\nfoobar\n\tfoobar')).toBe('foo\nbar\n\nfoobar\n\tfoobar') - }) - test('links', () => { - expect(plaintextThroughEditor('[test](foo)')).toBe('[test](foo)') - }) - test('images', () => { - expect(plaintextThroughEditor('![test](foo)')).toBe('![test](foo)') - }) + test('headlines', () => { + expect(plaintextThroughEditor('# Test')).toBe('# Test') + expect(plaintextThroughEditor('## Test')).toBe('## Test') + expect(plaintextThroughEditor('### Test')).toBe('### Test') + expect(plaintextThroughEditor('#### Test')).toBe('#### Test') + expect(plaintextThroughEditor('##### Test')).toBe('##### Test') + }) + test('inline format', () => { + expect(plaintextThroughEditor('**Test**')).toBe('**Test**') + expect(plaintextThroughEditor('__Test__')).toBe('__Test__') + expect(plaintextThroughEditor('_Test_')).toBe('_Test_') + expect(plaintextThroughEditor('~~Test~~')).toBe('~~Test~~') + }) + test('ul', () => { + expect(plaintextThroughEditor('- foo\n- bar')).toBe('- foo\n- bar') + expect(plaintextThroughEditor('- foo\n\n- bar')).toBe('- foo\n\n- bar') + expect(plaintextThroughEditor('- foo\n\n\n- bar')).toBe('- foo\n\n\n- bar') + }) + test('ol', () => { + expect(plaintextThroughEditor('1. foo\n2. bar')).toBe('1. foo\n2. bar') + }) + test('paragraph', () => { + expect(plaintextThroughEditor('foo\nbar\n\nfoobar\n\tfoobar')).toBe('foo\nbar\n\nfoobar\n\tfoobar') + }) + test('links', () => { + expect(plaintextThroughEditor('[test](foo)')).toBe('[test](foo)') + }) + test('images', () => { + expect(plaintextThroughEditor('![test](foo)')).toBe('![test](foo)') + }) }) describe('html as plain text', () => { - test('link', () => { - expect(plaintextThroughEditor('sdf')).toBe('sdf') - expect(plaintextThroughEditor('sdf')).toBe('sdf') + test('link', () => { + expect(plaintextThroughEditor('sdf')).toBe('sdf') + expect(plaintextThroughEditor('sdf')).toBe('sdf') - }) - test('special characters', () => { - expect(plaintextThroughEditor('"\';&.-#><')).toBe('"\';&.-#><') - expect(plaintextThroughEditor(xssFuzzVectors)).toBe(xssFuzzVectors) - }) -} ) + }) + test('special characters', () => { + expect(plaintextThroughEditor('"\';&.-#><')).toBe('"\';&.-#><') + expect(plaintextThroughEditor(xssFuzzVectors)).toBe(xssFuzzVectors) + }) +}) describe('regression tests', () => { test('tripple enter creates new lines at end (#6507)', () => { @@ -90,6 +90,6 @@ describe('regression tests', () => { tiptap.commands.enter() tiptap.commands.enter() tiptap.commands.enter() - expect(serializePlainText(tiptap.state.doc)).toEqual("\n\n\n") + expect(serializePlainText(tiptap.state.doc)).toEqual('\n\n\n') }) }) diff --git a/src/tests/plugins/extractHeadings.spec.js b/src/tests/plugins/extractHeadings.spec.js index a5b61aab48c..abc0fea68aa 100644 --- a/src/tests/plugins/extractHeadings.spec.js +++ b/src/tests/plugins/extractHeadings.spec.js @@ -46,10 +46,10 @@ describe('extractHeadings', () => { }) -function prepareDoc(content) { +const prepareDoc = (content) => { const editor = createCustomEditor({ content, - extensions: [ Heading ] + extensions: [Heading], }) return editor.state.doc } diff --git a/src/tests/plugins/extractLinkParagraphs.spec.js b/src/tests/plugins/extractLinkParagraphs.spec.js index ab5d7e0c6e3..0062acd58b1 100644 --- a/src/tests/plugins/extractLinkParagraphs.spec.js +++ b/src/tests/plugins/extractLinkParagraphs.spec.js @@ -23,7 +23,7 @@ describe('extractLinkParagraphs', () => { const doc = prepareDoc(content) const paragraphs = extractLinkParagraphs(doc) expect(paragraphs).toEqual([ - { offset: 0 , type: 'text-only', nodeSize: 6 } + { offset: 0, type: 'text-only', nodeSize: 6 }, ]) }) @@ -31,7 +31,7 @@ describe('extractLinkParagraphs', () => { const doc = prepareDoc(preview) const paragraphs = extractLinkParagraphs(doc) expect(paragraphs).toEqual([ - { offset: 0 , type: 'link-preview', nodeSize: 6 } + { offset: 0, type: 'link-preview', nodeSize: 6 }, ]) }) @@ -40,7 +40,7 @@ describe('extractLinkParagraphs', () => { const doc = prepareDoc(content) const paragraphs = extractLinkParagraphs(doc) expect(paragraphs).toEqual([ - { offset: 0 , type: 'text-only', nodeSize: 7 } + { offset: 0, type: 'text-only', nodeSize: 7 }, ]) }) @@ -50,8 +50,8 @@ describe('extractLinkParagraphs', () => { const doc = prepareDoc(content) const paragraphs = extractLinkParagraphs(doc) expect(paragraphs).toEqual([ - { offset: 0 , type: 'text-only', nodeSize: 6 }, - { offset: 6 , type: 'text-only', nodeSize: 6 } + { offset: 0, type: 'text-only', nodeSize: 6 }, + { offset: 6, type: 'text-only', nodeSize: 6 }, ]) }) @@ -60,8 +60,8 @@ describe('extractLinkParagraphs', () => { const doc = prepareDoc(content) const paragraphs = extractLinkParagraphs(doc) expect(paragraphs).toEqual([ - { offset: 0 , type: 'text-only', nodeSize: 6 }, - { offset: 6 , type: 'link-preview', nodeSize: 6 } + { offset: 0, type: 'text-only', nodeSize: 6 }, + { offset: 6, type: 'link-preview', nodeSize: 6 }, ]) }) @@ -102,10 +102,10 @@ describe('extractLinkParagraphs', () => { }) -function prepareDoc(content) { +const prepareDoc = (content) => { const editor = createCustomEditor({ content, - extensions: [ Link, Preview ] + extensions: [Link, Preview], }) return editor.state.doc } diff --git a/src/tests/plugins/linkBubble.spec.js b/src/tests/plugins/linkBubble.spec.js index a79dcfc1c05..d3a9b12fde7 100644 --- a/src/tests/plugins/linkBubble.spec.js +++ b/src/tests/plugins/linkBubble.spec.js @@ -11,37 +11,37 @@ describe('linkBubble prosemirror plugin', () => { test('signature', () => { expect(linkBubble).toBeInstanceOf(Function) - expect(new linkBubble()).toBeInstanceOf(Plugin) + expect(linkBubble()).toBeInstanceOf(Plugin) }) test('usage as plugin', () => { - const plugin = new linkBubble() - const state = createState({ plugins: [ plugin ] }) + const plugin = linkBubble() + const state = createState({ plugins: [plugin] }) expect(state.plugins).toContain(plugin) - expect(plugin.getState(state)).toEqual({"active": null}) + expect(plugin.getState(state)).toEqual({ active: null }) }) test('updates plugin state active on transaction', () => { - const plugin = new linkBubble() - const state = createState({ plugins: [ plugin ] }) + const plugin = linkBubble() + const state = createState({ plugins: [plugin] }) const dummy = { was: 'active' } const tr = state.tr.setMeta(plugin, { active: dummy }) const after = state.apply(tr) - expect(plugin.getState(after)).toEqual({"active": dummy}) + expect(plugin.getState(after)).toEqual({ active: dummy }) }) test('setActiveLink requires a link mark', () => { const noMarks = { marks: () => [] } expect(setActiveLink(noMarks)(null, null)).toBe(false) - const otherMark = { marks: () => [{type: {name: 'other'}}] } + const otherMark = { marks: () => [{ type: { name: 'other' } }] } expect(setActiveLink(otherMark)(null, null)).toBe(false) - const mark = { marks: () => [{type: {name: 'link'}}] } + const mark = { marks: () => [{ type: { name: 'link' } }] } expect(setActiveLink(mark)(null, null)).toBe(true) }) test('setActiveLink extracts the link mark', () => { - const plugin = new linkBubble() - const state = createState({ plugins: [ plugin ] }) + const plugin = linkBubble() + const state = createState({ plugins: [plugin] }) const flow = createFlow(state) const mark = { type: { name: 'link' } } const resolved = { marks: () => [mark] } @@ -51,14 +51,14 @@ describe('linkBubble prosemirror plugin', () => { }) test('hideLinkBubble requires an active menu bubble', () => { - const plugin = new linkBubble() - const state = createState({ plugins: [ plugin ] }) + const plugin = linkBubble() + const state = createState({ plugins: [plugin] }) expect(hideLinkBubble(state, null)).toBe(false) }) test('hideLinkBubble clears the active state', () => { - const plugin = new linkBubble() - const state = createState({ plugins: [ plugin ] }) + const plugin = linkBubble() + const state = createState({ plugins: [plugin] }) const flow = createFlow(state) const mark = { type: { name: 'link' } } const resolved = { marks: () => [mark] } @@ -71,7 +71,7 @@ describe('linkBubble prosemirror plugin', () => { }) // simulate the data flow in prosemirror -function createFlow(initialState) { +const createFlow = (initialState) => { let state = initialState return { get state() { @@ -83,7 +83,7 @@ function createFlow(initialState) { } } -function createState(options = {}) { +const createState = (options = {}) => { return EditorState.create({ schema, ...options, diff --git a/src/tests/plugins/searchDecorations.spec.js b/src/tests/plugins/searchDecorations.spec.js index 3ec3a4aa65e..b3ca9df450d 100644 --- a/src/tests/plugins/searchDecorations.spec.js +++ b/src/tests/plugins/searchDecorations.spec.js @@ -31,7 +31,7 @@ describe('search plugin', () => { testSearch( '

Hallo Welt

', '', - expected + expected, ) }) @@ -49,7 +49,7 @@ describe('search plugin', () => { testSearch( '

CATS birds crocodiles cats platypus CAts

', 'cAtS', - expected + expected, ) }) @@ -57,7 +57,7 @@ describe('search plugin', () => { const expected = { results: [ { from: 1, to: 4 }, - { from: 23, to: 26 } + { from: 23, to: 26 }, ], total: 2, index: 0, @@ -66,20 +66,20 @@ describe('search plugin', () => { testSearch( '

cat dinosaur bird dog cat

', 'cat', - expected + expected, ) }) it('finds matches in separate blocks', () => { - const doc = '

cat dinosaur bird dog cat

' + - '

dinosaur cat bird dinosaur cat

' + const doc = '

cat dinosaur bird dog cat

' + + '

dinosaur cat bird dinosaur cat

' const expected = { results: [ { from: 1, to: 4 }, { from: 23, to: 26 }, { from: 37, to: 40 }, - { from: 55, to: 58 } + { from: 55, to: 58 }, ], total: 5, index: 0, @@ -88,28 +88,16 @@ describe('search plugin', () => { testSearch( doc, 'cat', - expected + expected, ) }) }) -function testSearch(doc, query, expectedSearchResults) { - const editor = createCustomEditor({ - content: doc, - extensions: [], - }) - - const searchResults = { - expected: expectedSearchResults, - actual: runSearch(editor.state.doc, query), - } - - const decorationResults = { - expected: highlightResults(editor.state.doc, searchResults.expected.results), - actual: highlightResults(editor.state.doc, searchResults.actual.results), - } - - - expect(searchResults.actual.results).toEqual(searchResults.expected.results) - expect(decorationResults.actual.results).toEqual(decorationResults.expected.results) +const testSearch = (doc, query, expectedSearchResults) => { + const editor = createCustomEditor({ content: doc, extensions: [] }) + const searched = runSearch(editor.state.doc, query) + expect(searched) + .toHaveProperty('results', expectedSearchResults.results) + expect(highlightResults(editor.state.doc, searched.results)) + .toEqual(highlightResults(editor.state.doc, expectedSearchResults.results)) } diff --git a/src/tests/plugins/searchQuery.spec.js b/src/tests/plugins/searchQuery.spec.js index 7082e96f3ae..84a041a93fa 100644 --- a/src/tests/plugins/searchQuery.spec.js +++ b/src/tests/plugins/searchQuery.spec.js @@ -2,10 +2,14 @@ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { searchQuery } from '../../plugins/searchQuery.js' import { Plugin, EditorState } from '@tiptap/pm/state' import { schema } from '@tiptap/pm/schema-basic' -import { setSearchQuery, nextMatch, previousMatch } from '../../plugins/searchQuery.js' +import { + searchQuery, + setSearchQuery, + nextMatch, + previousMatch, +} from '../../plugins/searchQuery.js' describe('searchQuery plugin', () => { it('can set up plugin and state', () => { @@ -30,7 +34,7 @@ describe('searchQuery plugin', () => { const { plugin, state } = pluginSetup() const setSearch = setSearchQuery('lorem')(state) - let newState = state.apply(setSearch) + const newState = state.apply(setSearch) expect(plugin.getState(newState)).toEqual({ query: 'lorem', @@ -56,13 +60,13 @@ describe('searchQuery plugin', () => { newState = newState.apply(nextSearch) expect(plugin.getState(newState)).toEqual({ - query: 'lorem', // search query should be the same - matchAll: false, // matchAll is set to false - index: 1, // index is incremented to the next match + query: 'lorem', // search query should be the same + matchAll: false, // matchAll is set to false + index: 1, // index is incremented to the next match }) }) - it ('can accept previous match state', () => { + it('can accept previous match state', () => { const { plugin, state } = pluginSetup() const setSearch = setSearchQuery('lorem')(state) @@ -75,7 +79,7 @@ describe('searchQuery plugin', () => { matchAll: true, index: 0, }) - + newState = newState.apply(previousSearch) expect(plugin.getState(newState)).toEqual({ @@ -86,12 +90,12 @@ describe('searchQuery plugin', () => { }) }) -function pluginSetup() { - const plugin = new searchQuery() +const pluginSetup = () => { + const plugin = searchQuery() const state = EditorState.create({ schema, plugins: [plugin], }) return { plugin, state } -} \ No newline at end of file +} diff --git a/src/tests/services/AttachmentResolver.spec.js b/src/tests/services/AttachmentResolver.spec.js index b997bac02c6..da0ec4b953a 100644 --- a/src/tests/services/AttachmentResolver.spec.js +++ b/src/tests/services/AttachmentResolver.spec.js @@ -12,7 +12,7 @@ const sessionToken = 'mySessionToken' const a1name = 'group pic.jpg' const a1nameEncoded = 'group%20pic.jpg' const a2name = 'archive.tar.gz' -function initAttachmentResolver(args) { +const initAttachmentResolver = (args) => { const attachmentList = [{ fileId: 1234, name: a1name, @@ -21,7 +21,7 @@ function initAttachmentResolver(args) { mtime: 1, isImage: true, fullUrl: `http://nextcloud.local/apps/text/image?documentId=${fileId}&sessionId=${sessionId}&sessionToken=${sessionToken}&imageFileName=${a1nameEncoded}&preferRawImage=1"`, - previewUrl: `http://nextcloud.local/apps/text/image?documentId=${fileId}&sessionId=${sessionId}&sessionToken=${sessionToken}&imageFileName=${a1nameEncoded}"` + previewUrl: `http://nextcloud.local/apps/text/image?documentId=${fileId}&sessionId=${sessionId}&sessionToken=${sessionToken}&imageFileName=${a1nameEncoded}"`, }, { fileId: 1236, name: a2name, @@ -30,7 +30,7 @@ function initAttachmentResolver(args) { mtime: 1, isImage: false, fullUrl: `http://nextcloud.local/apps/text/media?documentId=${fileId}&sessionId=${sessionId}&sessionToken=${sessionToken}&mediaFileName=${a2name}"`, - previewUrl: `http://nextcloud.local/apps/text/mediaPreview?documentId=${fileId}&sessionId=${sessionId}&sessionToken=${sessionToken}&mediaFileName=${a2name}"` + previewUrl: `http://nextcloud.local/apps/text/mediaPreview?documentId=${fileId}&sessionId=${sessionId}&sessionToken=${sessionToken}&mediaFileName=${a2name}"`, }] const axiosSpy = jest.spyOn(axios, 'post').mockReturnValue({ data: attachmentList }) const resolver = new AttachmentResolver(args) @@ -49,7 +49,6 @@ describe('Image resolver', () => { uid: 'user-uid', } const currentDirectory = '/parentDir' - const shareToken = 'myShareToken' it('is a class with one constructor argument', () => { const resolver = new AttachmentResolver({ fileId }) @@ -88,7 +87,7 @@ describe('Image resolver', () => { }) it('handles non-native urls wia webdav', async () => { - const src = `/path/to/some image.png` + const src = '/path/to/some image.png' const resolver = new AttachmentResolver({ fileId, user, currentDirectory }) const attachment = await resolver.resolve(src) expect(attachment.isImage).toBe(true) diff --git a/src/tests/services/WebsocketPolyfill.spec.js b/src/tests/services/WebsocketPolyfill.spec.js index 6eb4ad72f3e..4f35a1bb7bb 100644 --- a/src/tests/services/WebsocketPolyfill.spec.js +++ b/src/tests/services/WebsocketPolyfill.spec.js @@ -18,6 +18,7 @@ describe('Init function', () => { const syncService = { on: jest.fn(), open: jest.fn(() => Promise.resolve({ version: 123, session: {} })) } const Polyfill = initWebSocketPolyfill(syncService) const websocket = new Polyfill('url') + expect(websocket).toBeInstanceOf(Polyfill) expect(syncService.on).toHaveBeenCalled() }) @@ -27,6 +28,7 @@ describe('Init function', () => { const initialSession = { } const Polyfill = initWebSocketPolyfill(syncService, fileId, initialSession) const websocket = new Polyfill('url') + expect(websocket).toBeInstanceOf(Polyfill) expect(syncService.open).toHaveBeenCalledWith({ fileId, initialSession }) }) @@ -36,13 +38,13 @@ describe('Init function', () => { open: jest.fn(() => Promise.resolve({ version: 123, session: {} })), sendSteps: async getData => getData(), } - const queue = [ 'initial' ] + const queue = ['initial'] const data = { dummy: 'data' } const Polyfill = initWebSocketPolyfill(syncService, null, null, queue) const websocket = new Polyfill('url') const result = websocket.send(data) expect(result).toBeInstanceOf(Promise) - expect(queue).toEqual([ 'initial' , data ]) + expect(queue).toEqual(['initial', data]) const dataSendOut = await result expect(queue).toEqual([]) expect(dataSendOut).toHaveProperty('awareness') @@ -57,16 +59,16 @@ describe('Init function', () => { open: jest.fn(() => Promise.resolve({ version: 123, session: {} })), sendSteps: jest.fn().mockRejectedValue('error before reading steps in sync service'), } - const queue = [ 'initial' ] + const queue = ['initial'] const data = { dummy: 'data' } const Polyfill = initWebSocketPolyfill(syncService, null, null, queue) const websocket = new Polyfill('url') const result = websocket.send(data) - expect(queue).toEqual([ 'initial' , data ]) + expect(queue).toEqual(['initial', data]) expect(result).toBeInstanceOf(Promise) const returned = await result expect(returned).toBeUndefined() - expect(queue).toEqual([ 'initial' , data ]) + expect(queue).toEqual(['initial', data]) }) it('handles reject after reading data', async () => { @@ -74,21 +76,21 @@ describe('Init function', () => { const syncService = { on: jest.fn(), open: jest.fn(() => Promise.resolve({ version: 123, session: {} })), - sendSteps: jest.fn().mockImplementation( async getData => { + sendSteps: jest.fn().mockImplementation(async getData => { getData() - throw 'error when sending in sync service' + throw new Error('error when sending in sync service') }), } - const queue = [ 'initial' ] + const queue = ['initial'] const data = { dummy: 'data' } const Polyfill = initWebSocketPolyfill(syncService, null, null, queue) const websocket = new Polyfill('url') const result = websocket.send(data) - expect(queue).toEqual([ 'initial' , data ]) + expect(queue).toEqual(['initial', data]) expect(result).toBeInstanceOf(Promise) const returned = await result expect(returned).toBeUndefined() - expect(queue).toEqual([ 'initial' , data ]) + expect(queue).toEqual(['initial', data]) }) it('queue survives a close', async () => { @@ -96,27 +98,27 @@ describe('Init function', () => { const syncService = { on: jest.fn(), open: jest.fn(() => Promise.resolve({ version: 123, session: {} })), - sendSteps: jest.fn().mockImplementation( async getData => { + sendSteps: jest.fn().mockImplementation(async getData => { getData() - throw 'error when sending in sync service' + throw new Error('error when sending in sync service') }), - sendStepsNow: jest.fn().mockImplementation( async getData => { + sendStepsNow: jest.fn().mockImplementation(async getData => { getData() - throw 'sendStepsNow error when sending' + throw new Error('sendStepsNow error when sending') }), off: jest.fn(), - close: jest.fn( async data => data ), + close: jest.fn(async data => data), } - const queue = [ 'initial' ] + const queue = ['initial'] const data = { dummy: 'data' } const Polyfill = initWebSocketPolyfill(syncService, null, null, queue) const websocket = new Polyfill('url') websocket.onclose = jest.fn() await websocket.send(data) const promise = websocket.close() - expect(queue).toEqual([ 'initial' , data ]) + expect(queue).toEqual(['initial', data]) await promise - expect(queue).toEqual([ 'initial' , data ]) + expect(queue).toEqual(['initial', data]) }) }) diff --git a/src/tests/setup.mjs b/src/tests/setup.mjs index 104f89199d6..3645f52ee37 100644 --- a/src/tests/setup.mjs +++ b/src/tests/setup.mjs @@ -3,8 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import 'core-js/stable'; -import 'regenerator-runtime/runtime'; import Vue from 'vue' global.t = jest.fn().mockImplementation((app, text) => text) @@ -54,13 +52,12 @@ global.OC = { }, L10N: { translate: global.t, - } + }, } global.OCA = {} global._oc_webroot = '' - Vue.prototype.t = global.t Vue.prototype.n = global.n Vue.prototype.OC = OC @@ -69,23 +66,27 @@ Vue.prototype.OCA = OCA // Mock ClipboardEvent and DragEvent as long as jsdom is used // https://github.com/ueberdosis/tiptap/issues/4455 class ClipboardEventMock extends Event { - constructor(type, eventInitDict) { - super(type, eventInitDict); - this.clipboardData = { - getData: jest.fn(), - setData: jest.fn(), - }; - } + + constructor(type, eventInitDict) { + super(type, eventInitDict) + this.clipboardData = { + getData: jest.fn(), + setData: jest.fn(), + } + } + } -global.ClipboardEvent = ClipboardEventMock; +global.ClipboardEvent = ClipboardEventMock class DragEventMock extends Event { - constructor(type, eventInitDict) { - super(type, eventInitDict); - this.dataTransfer = { - getData: jest.fn(), - setData: jest.fn(), - }; - } + + constructor(type, eventInitDict) { + super(type, eventInitDict) + this.dataTransfer = { + getData: jest.fn(), + setData: jest.fn(), + } + } + } -global.DragEvent = DragEventMock; +global.DragEvent = DragEventMock diff --git a/src/tests/tiptap.spec.js b/src/tests/tiptap.spec.js index b1adc70ed23..2b8b5a6898a 100644 --- a/src/tests/tiptap.spec.js +++ b/src/tests/tiptap.spec.js @@ -3,26 +3,26 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { createRichEditor } from '../EditorFactory' -import markdownit from '../markdownit' +import { createRichEditor } from '../EditorFactory.js' +import markdownit from '../markdownit/index.js' -const renderedHTML = ( markdown ) => { - const editor = createRichEditor() - editor.commands.setContent(markdownit.render(markdown)) - // Remove TrailingNode - return editor.getHTML().replace(/

<\/p>$/, '') +const renderedHTML = (markdown) => { + const editor = createRichEditor() + editor.commands.setContent(markdownit.render(markdown)) + // Remove TrailingNode + return editor.getHTML().replace(/

<\/p>$/, '') } describe('TipTap', () => { it('render softbreaks', () => { - const markdown = 'This\nis\none\nparagraph' + const markdown = 'This\nis\none\nparagraph' expect(renderedHTML(markdown)).toEqual(`

${markdown}

`) }) - it('render hardbreak', () => { - const markdown = 'Hard line break \nNext Paragraph' - expect(renderedHTML(markdown)).toEqual('

Hard line break
Next Paragraph

') - }) + it('render hardbreak', () => { + const markdown = 'Hard line break \nNext Paragraph' + expect(renderedHTML(markdown)).toEqual('

Hard line break
Next Paragraph

') + }) it('render taskList', () => { const markdown = '* [ ] item 1\n' diff --git a/src/tests/yjs.spec.js b/src/tests/yjs.spec.js index 7a770e824f3..eb638d4e738 100644 --- a/src/tests/yjs.spec.js +++ b/src/tests/yjs.spec.js @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import recorded from './fixtures/recorded' +import recorded from './fixtures/recorded.js' import { Doc, encodeStateAsUpdate } from 'yjs' import { decodeArrayBuffer } from '../helpers/base64.ts' import * as decoding from 'lib0/decoding' @@ -14,28 +14,25 @@ describe('recorded session', () => { const flattened = recorded.flat() const sync = flattened.filter(step => step.startsWith('AA')) const awareness = flattened.filter(step => /^A[QRSTU]/.test(step)) - function size(arr) { - return arr.reduce((total, cur) => total + cur.length, 0) - } - - function processStep(ydoc, step) { + const size = (arr) => arr.reduce((total, cur) => total + cur.length, 0) + const processStep = (ydoc, step) => { const buf = decodeArrayBuffer(step) const decoder = decoding.createDecoder(buf) const encoder = encoding.createEncoder() - const messageType = decoding.readVarUint(decoder) + decoding.readVarUint(decoder) const inType = syncProtocol.readSyncMessage( decoder, encoder, - ydoc + ydoc, ) if (!encoding.length(encoder)) { - return {inType, length: 0} + return { inType, length: 0 } } const binary = encoding.toUint8Array(encoder) const outBuf = decodeArrayBuffer(binary) const outDecoder = decoding.createDecoder(outBuf) const outType = decoding.peekVarUint(outDecoder) - return {inType, outType, length: encoding.length(encoder)} + return { inType, outType, length: encoding.length(encoder) } } test('original size', () => { @@ -77,7 +74,7 @@ describe('recorded session', () => { const syncsOfType = type => sync.filter((step) => { const buf = decodeArrayBuffer(step) const decoder = decoding.createDecoder(buf) - const messageType = decoding.readVarUint(decoder) + decoding.readVarUint(decoder) return decoding.peekVarUint(decoder) === type }) const step1s = syncsOfType(syncProtocol.messageYjsSyncStep1) @@ -94,7 +91,7 @@ describe('recorded session', () => { test('read messages', () => { const ydoc = new Doc() const responses = sync.map(step => processStep(ydoc, step)) - responses.forEach(({inType, length}) => { + responses.forEach(({ inType, length }) => { expect([0, 1, 2]).toContain(inType) if (inType !== syncProtocol.messageYjsSyncStep1) { expect(length).toBe(0) @@ -107,7 +104,7 @@ describe('recorded session', () => { test('analyse responses', () => { const ydoc = new Doc() const responses = sync.map(step => processStep(ydoc, step)) - responses.forEach(({inType, outType}) => { + responses.forEach(({ inType, outType }) => { if (inType === syncProtocol.messageYjsSyncStep1) { expect(outType).toBe(syncProtocol.messageYjsSyncStep2) } @@ -128,7 +125,7 @@ describe('recorded session', () => { const ydoc = new Doc() const withoutQueries = sync.filter(step => step > 'AAE') const responses = withoutQueries.map(step => processStep(ydoc, step)) - responses.forEach(({inType, outType}) => { + responses.forEach(({ inType, outType }) => { if (inType === syncProtocol.messageYjsSyncStep1) { expect(outType).toBe(syncProtocol.messageYjsSyncStep2) }