From 9fbd73bf855ad40ba7937bca02bf6865bb5bb1b7 Mon Sep 17 00:00:00 2001 From: grnd-alt Date: Mon, 15 Apr 2024 15:10:45 +0200 Subject: [PATCH 1/9] remove old link when editing Signed-off-by: grnd-alt --- src/components/Menu/ActionInsertLink.vue | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/Menu/ActionInsertLink.vue b/src/components/Menu/ActionInsertLink.vue index 8a918c09dcb..8e2dd32fecb 100644 --- a/src/components/Menu/ActionInsertLink.vue +++ b/src/components/Menu/ActionInsertLink.vue @@ -191,6 +191,9 @@ export default { const chain = this.$editor.chain() // Check if any text is selected, if not insert the link using the given text property if (this.$editor.view.state?.selection.empty) { + if (this.state.active) { + chain.deleteNode('paragraph') + } chain.insertContent({ type: 'paragraph', content: [{ From 2b1ce9a633d1bac5b34ff58b4feb9bb01f0bc787 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 17 Apr 2024 15:16:00 +0200 Subject: [PATCH 2/9] refactor(links): add insertOrSetLink command Signed-off-by: Max --- src/components/Menu/ActionInsertLink.vue | 22 +---------------- src/marks/Link.js | 31 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/components/Menu/ActionInsertLink.vue b/src/components/Menu/ActionInsertLink.vue index 8e2dd32fecb..f69008e455d 100644 --- a/src/components/Menu/ActionInsertLink.vue +++ b/src/components/Menu/ActionInsertLink.vue @@ -189,27 +189,7 @@ export default { // Avoid issues when parsing urls later on in markdown that might be entered in an invalid format (e.g. "mailto: example@example.com") const href = url.replaceAll(' ', '%20') const chain = this.$editor.chain() - // Check if any text is selected, if not insert the link using the given text property - if (this.$editor.view.state?.selection.empty) { - if (this.state.active) { - chain.deleteNode('paragraph') - } - chain.insertContent({ - type: 'paragraph', - content: [{ - type: 'text', - marks: [{ - type: 'link', - attrs: { - href, - }, - }], - text, - }], - }) - } else { - chain.setLink({ href }) - } + chain.insertOrSetLink(this.state.active, text, { href }) chain.focus().run() }, /** diff --git a/src/marks/Link.js b/src/marks/Link.js index 1878f43958b..1b825e66670 100644 --- a/src/marks/Link.js +++ b/src/marks/Link.js @@ -87,6 +87,37 @@ const Link = TipTapLink.extend({ }), ] }, + addCommands() { + return { + /** + * Update the target of existing links. + * Insert a link if there currently is none. + * + */ + insertOrSetLink: (active, text, attrs) => ({ state, chain, commands }) => { + // Check if any text is selected, + // if not insert the link using the given text property + if (state.selection.empty) { + if (active) { + commands.deleteNode('paragraph') + } + return chain().insertContent({ + type: 'paragraph', + content: [{ + type: 'text', + marks: [{ + type: 'link', + attrs, + }], + text, + }], + }) + } else { + return commands.setLink(attrs) + } + }, + } + }, addProseMirrorPlugins() { const plugins = this.parent() From ed3a6d811977f3e5cc6ffcaa82f90b482ffebeea Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 17 Apr 2024 15:26:56 +0200 Subject: [PATCH 3/9] refactor(cy): move Links.spec.js into e2e folder It is a proper e2e test driving the ui. Signed-off-by: Max --- cypress/e2e/{nodes => }/Links.spec.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cypress/e2e/{nodes => }/Links.spec.js (100%) diff --git a/cypress/e2e/nodes/Links.spec.js b/cypress/e2e/Links.spec.js similarity index 100% rename from cypress/e2e/nodes/Links.spec.js rename to cypress/e2e/Links.spec.js From 3c7aee66d53c4ede27e1de8006635963f36f8f3b Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 17 Apr 2024 15:49:13 +0200 Subject: [PATCH 4/9] test(cy): initial test for the link mark Signed-off-by: Max --- cypress/e2e/marks/Link.spec.js | 94 ++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 cypress/e2e/marks/Link.spec.js diff --git a/cypress/e2e/marks/Link.spec.js b/cypress/e2e/marks/Link.spec.js new file mode 100644 index 00000000000..98aa1ec66c3 --- /dev/null +++ b/cypress/e2e/marks/Link.spec.js @@ -0,0 +1,94 @@ +/* eslint-disable no-unused-expressions */ +/** + * @copyright Copyright (c) 2024 Max + * + * @author Max + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +import Markdown from './../../../src/extensions/Markdown.js' +import { Italic, Link } from './../../../src/marks/index.js' +import { createCustomEditor } from './../../support/components.js' +import { loadMarkdown, expectMarkdown } from '../nodes/helpers.js' + +describe('Link marks', { retries: 0 }, () => { + + const editor = createCustomEditor({ + content: '', + extensions: [ + Markdown, + Link, + Italic, + ], + }) + + describe('insertOrSetLink command', { retries: 0 }, () => { + + it('is available in commands', () => { + expect(editor.commands).to.have.property('insertOrSetLink') + }) + + it('can run on normal paragraph', () => { + prepareEditor('hello\n', 3) + expect(editor.can().insertOrSetLink()).to.be.ok + }) + + it('will insert a link in a normal paragraph', () => { + prepareEditor('hello\n', 3) + editor.commands.insertOrSetLink(false, 'https://nextcloud.com', { href: 'https://nextcloud.com' }) + expectMarkdown(editor, 'he\n\n\n\nllo') + }) + + }) + + /** + * Expect a link in the editor. + */ + function expectLink() { + expect(getParentNode().type.name).to.equal('paragraph') + expect(getParentNode().attrs.href).to.equal('https://nextcloud.com') + expect(getMark().attrs.href).to.equal('https://nextcloud.com') + } + + /** + * + */ + function getParentNode() { + const { state: { selection } } = editor + return selection.$head.parent + } + + /** + * + */ + function getMark() { + const { state: { selection } } = editor + console.info(selection.$head) + return selection.$head.nodeAfter.marks[0] + } + + /** + * + * @param input + */ + function prepareEditor(input, position = 1) { + loadMarkdown(editor, input) + editor.commands.setTextSelection( position ) + } + +}) From 6539ff761094dab1eb21d9677fc7d8f0ccf15031 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 17 Apr 2024 17:02:29 +0200 Subject: [PATCH 5/9] test(cy): adjust import path in moved Links.spec.js Signed-off-by: Max --- cypress/e2e/Links.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/e2e/Links.spec.js b/cypress/e2e/Links.spec.js index 13d6db365e6..36cfc17a57c 100644 --- a/cypress/e2e/Links.spec.js +++ b/cypress/e2e/Links.spec.js @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { randUser } from '../../utils/index.js' +import { randUser } from '../utils/index.js' const user = randUser() const fileName = 'empty.md' From c1ef403662c85b18f54aafc97964636d99388e8d Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 17 Apr 2024 18:08:08 +0200 Subject: [PATCH 6/9] refactor(link): use isMarkActive in insertOrSetLink This way we do not hand in the active state and avoid inconsistencies. Signed-off-by: Max --- cypress/e2e/marks/Link.spec.js | 2 +- src/components/Menu/ActionInsertLink.vue | 2 +- src/marks/Link.js | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cypress/e2e/marks/Link.spec.js b/cypress/e2e/marks/Link.spec.js index 98aa1ec66c3..f2ccb7dd2d7 100644 --- a/cypress/e2e/marks/Link.spec.js +++ b/cypress/e2e/marks/Link.spec.js @@ -50,7 +50,7 @@ describe('Link marks', { retries: 0 }, () => { it('will insert a link in a normal paragraph', () => { prepareEditor('hello\n', 3) - editor.commands.insertOrSetLink(false, 'https://nextcloud.com', { href: 'https://nextcloud.com' }) + editor.commands.insertOrSetLink('https://nextcloud.com', { href: 'https://nextcloud.com' }) expectMarkdown(editor, 'he\n\n\n\nllo') }) diff --git a/src/components/Menu/ActionInsertLink.vue b/src/components/Menu/ActionInsertLink.vue index f69008e455d..8c50d815076 100644 --- a/src/components/Menu/ActionInsertLink.vue +++ b/src/components/Menu/ActionInsertLink.vue @@ -189,7 +189,7 @@ export default { // Avoid issues when parsing urls later on in markdown that might be entered in an invalid format (e.g. "mailto: example@example.com") const href = url.replaceAll(' ', '%20') const chain = this.$editor.chain() - chain.insertOrSetLink(this.state.active, text, { href }) + chain.insertOrSetLink(text, { href }) chain.focus().run() }, /** diff --git a/src/marks/Link.js b/src/marks/Link.js index 1b825e66670..97e57b631c4 100644 --- a/src/marks/Link.js +++ b/src/marks/Link.js @@ -7,6 +7,7 @@ import { markInputRule } from '@tiptap/core' import TipTapLink from '@tiptap/extension-link' import { domHref, parseHref } from './../helpers/links.js' import { linkClicking } from '../plugins/links.js' +import { isMarkActive } from '@tiptap/core' const PROTOCOLS_TO_LINK_TO = ['http:', 'https:', 'mailto:', 'tel:'] @@ -94,11 +95,11 @@ const Link = TipTapLink.extend({ * Insert a link if there currently is none. * */ - insertOrSetLink: (active, text, attrs) => ({ state, chain, commands }) => { + insertOrSetLink: (text, attrs) => ({ state, chain, commands }) => { // Check if any text is selected, // if not insert the link using the given text property if (state.selection.empty) { - if (active) { + if (isMarkActive(state, this.name)) { commands.deleteNode('paragraph') } return chain().insertContent({ From 819ea959c1c4ab8a6dac2a9e7fe1e6f8191b3dd3 Mon Sep 17 00:00:00 2001 From: grnd-alt Date: Tue, 11 Feb 2025 11:38:39 +0100 Subject: [PATCH 7/9] fix: replace link correctly Signed-off-by: grnd-alt --- cypress/e2e/marks/Link.spec.js | 69 ++++----------------- src/marks/Link.js | 31 +++++----- src/tests/marks/Link.spec.js | 106 +++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 75 deletions(-) create mode 100644 src/tests/marks/Link.spec.js diff --git a/cypress/e2e/marks/Link.spec.js b/cypress/e2e/marks/Link.spec.js index f2ccb7dd2d7..59bb3402013 100644 --- a/cypress/e2e/marks/Link.spec.js +++ b/cypress/e2e/marks/Link.spec.js @@ -1,24 +1,6 @@ -/* eslint-disable no-unused-expressions */ /** - * @copyright Copyright (c) 2024 Max - * - * @author Max - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import Markdown from './../../../src/extensions/Markdown.js' @@ -27,68 +9,37 @@ import { createCustomEditor } from './../../support/components.js' import { loadMarkdown, expectMarkdown } from '../nodes/helpers.js' describe('Link marks', { retries: 0 }, () => { - const editor = createCustomEditor({ content: '', - extensions: [ - Markdown, - Link, - Italic, - ], + extensions: [Markdown, Link, Italic], }) describe('insertOrSetLink command', { retries: 0 }, () => { - it('is available in commands', () => { expect(editor.commands).to.have.property('insertOrSetLink') }) it('can run on normal paragraph', () => { prepareEditor('hello\n', 3) - expect(editor.can().insertOrSetLink()).to.be.ok + expect(editor.can().insertOrSetLink()).toBe(true) }) it('will insert a link in a normal paragraph', () => { prepareEditor('hello\n', 3) - editor.commands.insertOrSetLink('https://nextcloud.com', { href: 'https://nextcloud.com' }) + editor.commands.insertOrSetLink('https://nextcloud.com', { + href: 'https://nextcloud.com', + }) expectMarkdown(editor, 'he\n\n\n\nllo') }) - }) - /** - * Expect a link in the editor. - */ - function expectLink() { - expect(getParentNode().type.name).to.equal('paragraph') - expect(getParentNode().attrs.href).to.equal('https://nextcloud.com') - expect(getMark().attrs.href).to.equal('https://nextcloud.com') - } - - /** - * - */ - function getParentNode() { - const { state: { selection } } = editor - return selection.$head.parent - } - /** * - */ - function getMark() { - const { state: { selection } } = editor - console.info(selection.$head) - return selection.$head.nodeAfter.marks[0] - } - - /** - * - * @param input + * @param {*} input markdown content + * @param {*} position cursor pos */ function prepareEditor(input, position = 1) { loadMarkdown(editor, input) - editor.commands.setTextSelection( position ) + editor.commands.setTextSelection(position) } - }) diff --git a/src/marks/Link.js b/src/marks/Link.js index 97e57b631c4..5b460b5fb5a 100644 --- a/src/marks/Link.js +++ b/src/marks/Link.js @@ -3,11 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { markInputRule } from '@tiptap/core' import TipTapLink from '@tiptap/extension-link' import { domHref, parseHref } from './../helpers/links.js' import { linkClicking } from '../plugins/links.js' -import { isMarkActive } from '@tiptap/core' +import { markInputRule, getMarkRange, isMarkActive } from '@tiptap/core' const PROTOCOLS_TO_LINK_TO = ['http:', 'https:', 'mailto:', 'tel:'] @@ -90,28 +89,28 @@ const Link = TipTapLink.extend({ }, addCommands() { return { - /** - * Update the target of existing links. - * Insert a link if there currently is none. - * - */ insertOrSetLink: (text, attrs) => ({ state, chain, commands }) => { // Check if any text is selected, // if not insert the link using the given text property if (state.selection.empty) { if (isMarkActive(state, this.name)) { - commands.deleteNode('paragraph') + + // get current href to check what to replace, assumes there's only one link mark on the anchor + let href = '' + state.selection.$anchor.marks().forEach(item => { + if (item.attrs.href && item.type.name === 'link') { + href = item.attrs.href + } + }) + commands.deleteRange(getMarkRange(state.selection.$anchor, state.schema.marks.link, { href })) } return chain().insertContent({ - type: 'paragraph', - content: [{ - type: 'text', - marks: [{ - type: 'link', - attrs, - }], - text, + type: 'text', + marks: [{ + type: 'link', + attrs, }], + text, }) } else { return commands.setLink(attrs) diff --git a/src/tests/marks/Link.spec.js b/src/tests/marks/Link.spec.js new file mode 100644 index 00000000000..547f22a7a91 --- /dev/null +++ b/src/tests/marks/Link.spec.js @@ -0,0 +1,106 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import Link from './../../marks/Link.js' +import Underline from '../../marks/Underline.js' +import createCustomEditor from '../testHelpers/createCustomEditor.ts' + +describe('Link extension integrated in the editor', () => { + it('should have link available in commands', () => { + const editor = createCustomEditor('

Test HELLO WORLD

', [Link]) + expect(editor.commands).toHaveProperty('insertOrSetLink') + }) + + it('should update link if anchor has mark', () => { + const editor = createCustomEditor( + '

Test HELLO WORLD

', + [Link, Underline], + ) + editor.commands.setTextSelection(3) + editor.commands.insertOrSetLink('updated.de', { href: 'updated.de' }) + expect(editor.getJSON()).toEqual({ + content: [ + { + content: [ + { + marks: [ + { attrs: { href: 'updated.de', title: null }, type: 'link' }, + ], + text: 'updated.de', + type: 'text', + }, + { text: ' HELLO WORLD', type: 'text' }, + ], + type: 'paragraph', + }, + ], + type: 'doc', + }) + }) + + it('Should only update link the anchor is on', () => { + const editor = createCustomEditor( + '

Testsecond link

', + [Link], + ) + editor.commands.setTextSelection(3) + editor.commands.insertOrSetLink('updated.de', { href: 'updated.de' }) + expect(editor.getJSON()).toEqual({ + content: [ + { + content: [ + { + marks: [ + { attrs: { href: 'updated.de', title: null }, type: 'link' }, + ], + text: 'updated.de', + type: 'text', + }, + { + marks: [ + { + attrs: { + href: 'not-nextcloud.com', + title: null, + }, + type: 'link', + }, + ], + text: 'second link', + type: 'text', + }, + ], + type: 'paragraph', + }, + ], + type: 'doc', + }) + }) + + it('should insert new link if none at anchor', () => { + const editor = createCustomEditor( + '

Test HELLO WORLD

', + [Link], + ) + editor.commands.setTextSelection(10) + expect(editor.getJSON()).toEqual({ + content: [ + { + content: [ + { + marks: [ + { attrs: { href: 'nextcloud.com', title: null }, type: 'link' }, + ], + text: 'Test', + type: 'text', + }, + { text: ' HELLO WORLD', type: 'text' }, + ], + type: 'paragraph', + }, + ], + type: 'doc', + }) + }) +}) From 0c3bcadb09245e387b9ecf33d8d4169672495ec6 Mon Sep 17 00:00:00 2001 From: grnd-alt Date: Mon, 10 Mar 2025 14:18:13 +0100 Subject: [PATCH 8/9] fix: dont overwrite other link commands Signed-off-by: grnd-alt --- cypress/e2e/marks/Link.spec.js | 2 +- src/marks/Link.js | 22 +++++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/cypress/e2e/marks/Link.spec.js b/cypress/e2e/marks/Link.spec.js index 59bb3402013..03e53dffe30 100644 --- a/cypress/e2e/marks/Link.spec.js +++ b/cypress/e2e/marks/Link.spec.js @@ -21,7 +21,7 @@ describe('Link marks', { retries: 0 }, () => { it('can run on normal paragraph', () => { prepareEditor('hello\n', 3) - expect(editor.can().insertOrSetLink()).toBe(true) + expect(editor.can().insertOrSetLink().run()).to.equal(true) }) it('will insert a link in a normal paragraph', () => { diff --git a/src/marks/Link.js b/src/marks/Link.js index 5b460b5fb5a..ffe5001579c 100644 --- a/src/marks/Link.js +++ b/src/marks/Link.js @@ -89,6 +89,7 @@ const Link = TipTapLink.extend({ }, addCommands() { return { + ...this.parent?.(), insertOrSetLink: (text, attrs) => ({ state, chain, commands }) => { // Check if any text is selected, // if not insert the link using the given text property @@ -103,14 +104,25 @@ const Link = TipTapLink.extend({ } }) commands.deleteRange(getMarkRange(state.selection.$anchor, state.schema.marks.link, { href })) + return chain().insertContent({ + type: 'text', + marks: [{ + type: 'link', + attrs, + }], + text, + }) } return chain().insertContent({ - type: 'text', - marks: [{ - type: 'link', - attrs, + type: 'paragraph', + content: [{ + type: 'text', + marks: [{ + type: 'link', + attrs, + }], + text, }], - text, }) } else { return commands.setLink(attrs) From 08a528b9384cc1b98dfe18d5a83696a8002b0d74 Mon Sep 17 00:00:00 2001 From: grnd-alt Date: Wed, 19 Mar 2025 13:47:54 +0100 Subject: [PATCH 9/9] use snapshots for long test results Signed-off-by: grnd-alt --- src/tests/marks/Link.spec.js | 72 +--------- .../marks/__snapshots__/Link.spec.js.snap | 127 ++++++++++++++++++ 2 files changed, 132 insertions(+), 67 deletions(-) create mode 100644 src/tests/marks/__snapshots__/Link.spec.js.snap diff --git a/src/tests/marks/Link.spec.js b/src/tests/marks/Link.spec.js index 547f22a7a91..ee28d092e11 100644 --- a/src/tests/marks/Link.spec.js +++ b/src/tests/marks/Link.spec.js @@ -19,63 +19,17 @@ describe('Link extension integrated in the editor', () => { ) editor.commands.setTextSelection(3) editor.commands.insertOrSetLink('updated.de', { href: 'updated.de' }) - expect(editor.getJSON()).toEqual({ - content: [ - { - content: [ - { - marks: [ - { attrs: { href: 'updated.de', title: null }, type: 'link' }, - ], - text: 'updated.de', - type: 'text', - }, - { text: ' HELLO WORLD', type: 'text' }, - ], - type: 'paragraph', - }, - ], - type: 'doc', - }) + expect(editor.getJSON()).toMatchSnapshot() }) it('Should only update link the anchor is on', () => { const editor = createCustomEditor( - '

Testsecond link

', + '

Testsecond link

', [Link], ) editor.commands.setTextSelection(3) editor.commands.insertOrSetLink('updated.de', { href: 'updated.de' }) - expect(editor.getJSON()).toEqual({ - content: [ - { - content: [ - { - marks: [ - { attrs: { href: 'updated.de', title: null }, type: 'link' }, - ], - text: 'updated.de', - type: 'text', - }, - { - marks: [ - { - attrs: { - href: 'not-nextcloud.com', - title: null, - }, - type: 'link', - }, - ], - text: 'second link', - type: 'text', - }, - ], - type: 'paragraph', - }, - ], - type: 'doc', - }) + expect(editor.getJSON()).toMatchSnapshot() }) it('should insert new link if none at anchor', () => { @@ -84,23 +38,7 @@ describe('Link extension integrated in the editor', () => { [Link], ) editor.commands.setTextSelection(10) - expect(editor.getJSON()).toEqual({ - content: [ - { - content: [ - { - marks: [ - { attrs: { href: 'nextcloud.com', title: null }, type: 'link' }, - ], - text: 'Test', - type: 'text', - }, - { text: ' HELLO WORLD', type: 'text' }, - ], - type: 'paragraph', - }, - ], - type: 'doc', - }) + editor.commands.insertOrSetLink('new link', { href: 'https://nextcloud.com' }) + expect(editor.getJSON()).toMatchSnapshot() }) }) diff --git a/src/tests/marks/__snapshots__/Link.spec.js.snap b/src/tests/marks/__snapshots__/Link.spec.js.snap new file mode 100644 index 00000000000..ce88378678f --- /dev/null +++ b/src/tests/marks/__snapshots__/Link.spec.js.snap @@ -0,0 +1,127 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Link extension integrated in the editor > Should only update link the anchor is on 1`] = ` +{ + "content": [ + { + "content": [ + { + "marks": [ + { + "attrs": { + "href": "updated.de", + "title": null, + }, + "type": "link", + }, + ], + "text": "updated.de", + "type": "text", + }, + { + "marks": [ + { + "attrs": { + "href": "not-nextcloud.com", + "title": null, + }, + "type": "link", + }, + ], + "text": "second link", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "doc", +} +`; + +exports[`Link extension integrated in the editor > should insert new link if none at anchor 1`] = ` +{ + "content": [ + { + "content": [ + { + "marks": [ + { + "attrs": { + "href": "nextcloud.com", + "title": null, + }, + "type": "link", + }, + ], + "text": "Test", + "type": "text", + }, + { + "text": " HELL", + "type": "text", + }, + ], + "type": "paragraph", + }, + { + "content": [ + { + "marks": [ + { + "attrs": { + "href": "https://nextcloud.com", + "title": null, + }, + "type": "link", + }, + ], + "text": "new link", + "type": "text", + }, + ], + "type": "paragraph", + }, + { + "content": [ + { + "text": "O WORLD", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "doc", +} +`; + +exports[`Link extension integrated in the editor > should update link if anchor has mark 1`] = ` +{ + "content": [ + { + "content": [ + { + "marks": [ + { + "attrs": { + "href": "updated.de", + "title": null, + }, + "type": "link", + }, + ], + "text": "updated.de", + "type": "text", + }, + { + "text": " HELLO WORLD", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "doc", +} +`;