2022-05-05 15:08:47 +00:00
import { GlLink , GlForm } from '@gitlab/ui' ;
2022-04-28 00:08:57 +00:00
import { BubbleMenu } from '@tiptap/vue-2' ;
import { mountExtended } from 'helpers/vue_test_utils_helper' ;
2022-08-19 00:12:09 +00:00
import LinkBubbleMenu from '~/content_editor/components/bubble_menus/link_bubble_menu.vue' ;
2022-04-28 00:08:57 +00:00
import eventHubFactory from '~/helpers/event_hub_factory' ;
import Link from '~/content_editor/extensions/link' ;
import { createTestEditor , emitEditorEvent } from '../../test_utils' ;
const createFakeEvent = ( ) => ( { preventDefault : jest . fn ( ) , stopPropagation : jest . fn ( ) } ) ;
2022-08-19 00:12:09 +00:00
describe ( 'content_editor/components/bubble_menus/link_bubble_menu' , ( ) => {
2022-04-28 00:08:57 +00:00
let wrapper ;
let tiptapEditor ;
2022-05-05 15:08:47 +00:00
let contentEditor ;
2022-04-28 00:08:57 +00:00
let bubbleMenu ;
let eventHub ;
const buildEditor = ( ) => {
tiptapEditor = createTestEditor ( { extensions : [ Link ] } ) ;
2022-05-06 00:07:56 +00:00
contentEditor = { resolveUrl : jest . fn ( ) } ;
2022-04-28 00:08:57 +00:00
eventHub = eventHubFactory ( ) ;
} ;
const buildWrapper = ( ) => {
wrapper = mountExtended ( LinkBubbleMenu , {
provide : {
tiptapEditor ,
2022-05-05 15:08:47 +00:00
contentEditor ,
2022-04-28 00:08:57 +00:00
eventHub ,
} ,
} ) ;
} ;
const expectLinkButtonsToExist = ( exist = true ) => {
expect ( wrapper . findComponent ( GlLink ) . exists ( ) ) . toBe ( exist ) ;
expect ( wrapper . findByTestId ( 'copy-link-url' ) . exists ( ) ) . toBe ( exist ) ;
expect ( wrapper . findByTestId ( 'edit-link' ) . exists ( ) ) . toBe ( exist ) ;
expect ( wrapper . findByTestId ( 'remove-link' ) . exists ( ) ) . toBe ( exist ) ;
} ;
beforeEach ( async ( ) => {
buildEditor ( ) ;
buildWrapper ( ) ;
tiptapEditor
. chain ( )
. insertContent (
'Download <a href="/path/to/project/-/wikis/uploads/my_file.pdf" data-canonical-src="uploads/my_file.pdf" title="Click here to download">PDF File</a>' ,
)
. setTextSelection ( 14 ) // put cursor in the middle of the link
. run ( ) ;
await emitEditorEvent ( { event : 'transaction' , tiptapEditor } ) ;
bubbleMenu = wrapper . findComponent ( BubbleMenu ) ;
} ) ;
afterEach ( ( ) => {
wrapper . destroy ( ) ;
} ) ;
it ( 'renders bubble menu component' , async ( ) => {
expect ( bubbleMenu . props ( 'editor' ) ) . toBe ( tiptapEditor ) ;
expect ( bubbleMenu . classes ( ) ) . toEqual ( [ 'gl-shadow' , 'gl-rounded-base' , 'gl-bg-white' ] ) ;
} ) ;
it ( 'shows a clickable link to the URL in the link node' , async ( ) => {
const link = wrapper . findComponent ( GlLink ) ;
expect ( link . attributes ( ) ) . toEqual (
expect . objectContaining ( {
href : '/path/to/project/-/wikis/uploads/my_file.pdf' ,
'aria-label' : 'uploads/my_file.pdf' ,
title : 'uploads/my_file.pdf' ,
target : '_blank' ,
} ) ,
) ;
expect ( link . text ( ) ) . toBe ( 'uploads/my_file.pdf' ) ;
} ) ;
describe ( 'copy button' , ( ) => {
it ( 'copies the canonical link to clipboard' , async ( ) => {
jest . spyOn ( navigator . clipboard , 'writeText' ) ;
await wrapper . findByTestId ( 'copy-link-url' ) . vm . $emit ( 'click' ) ;
expect ( navigator . clipboard . writeText ) . toHaveBeenCalledWith ( 'uploads/my_file.pdf' ) ;
} ) ;
} ) ;
describe ( 'remove link button' , ( ) => {
it ( 'removes the link' , async ( ) => {
await wrapper . findByTestId ( 'remove-link' ) . vm . $emit ( 'click' ) ;
expect ( tiptapEditor . getHTML ( ) ) . toBe ( '<p>Download PDF File</p>' ) ;
} ) ;
} ) ;
describe ( 'for a placeholder link' , ( ) => {
beforeEach ( async ( ) => {
tiptapEditor
. chain ( )
. clearContent ( )
. insertContent ( 'Dummy link' )
. selectAll ( )
. setLink ( { href : '' } )
. setTextSelection ( 4 )
. run ( ) ;
await emitEditorEvent ( { event : 'transaction' , tiptapEditor } ) ;
} ) ;
it ( 'directly opens the edit form for a placeholder link' , async ( ) => {
expectLinkButtonsToExist ( false ) ;
expect ( wrapper . findComponent ( GlForm ) . exists ( ) ) . toBe ( true ) ;
} ) ;
it ( 'removes the link on clicking apply (if no change)' , async ( ) => {
await wrapper . findComponent ( GlForm ) . vm . $emit ( 'submit' , createFakeEvent ( ) ) ;
expect ( tiptapEditor . getHTML ( ) ) . toBe ( '<p>Dummy link</p>' ) ;
} ) ;
it ( 'removes the link on clicking cancel' , async ( ) => {
await wrapper . findByTestId ( 'cancel-link' ) . vm . $emit ( 'click' ) ;
expect ( tiptapEditor . getHTML ( ) ) . toBe ( '<p>Dummy link</p>' ) ;
} ) ;
} ) ;
describe ( 'edit button' , ( ) => {
let linkHrefInput ;
let linkTitleInput ;
beforeEach ( async ( ) => {
await wrapper . findByTestId ( 'edit-link' ) . vm . $emit ( 'click' ) ;
2022-05-05 15:08:47 +00:00
linkHrefInput = wrapper . findByTestId ( 'link-href' ) ;
linkTitleInput = wrapper . findByTestId ( 'link-title' ) ;
2022-04-28 00:08:57 +00:00
} ) ;
it ( 'hides the link and copy/edit/remove link buttons' , async ( ) => {
expectLinkButtonsToExist ( false ) ;
} ) ;
it ( 'shows a form to edit the link' , ( ) => {
expect ( wrapper . findComponent ( GlForm ) . exists ( ) ) . toBe ( true ) ;
expect ( linkHrefInput . element . value ) . toBe ( 'uploads/my_file.pdf' ) ;
expect ( linkTitleInput . element . value ) . toBe ( 'Click here to download' ) ;
} ) ;
it ( 'extends selection to select the entire link' , ( ) => {
const { from , to } = tiptapEditor . state . selection ;
expect ( from ) . toBe ( 10 ) ;
expect ( to ) . toBe ( 18 ) ;
} ) ;
it ( 'shows the copy/edit/remove link buttons again if selection changes to another non-link and then back again to a link' , async ( ) => {
expectLinkButtonsToExist ( false ) ;
tiptapEditor . commands . setTextSelection ( 3 ) ;
await emitEditorEvent ( { event : 'transaction' , tiptapEditor } ) ;
2022-05-05 15:08:47 +00:00
tiptapEditor . commands . setTextSelection ( 14 ) ;
await emitEditorEvent ( { event : 'transaction' , tiptapEditor } ) ;
2022-04-28 00:08:57 +00:00
expectLinkButtonsToExist ( true ) ;
expect ( wrapper . findComponent ( GlForm ) . exists ( ) ) . toBe ( false ) ;
} ) ;
describe ( 'after making changes in the form and clicking apply' , ( ) => {
beforeEach ( async ( ) => {
linkHrefInput . setValue ( 'https://google.com' ) ;
linkTitleInput . setValue ( 'Search Google' ) ;
2022-05-06 00:07:56 +00:00
contentEditor . resolveUrl . mockResolvedValue ( 'https://google.com' ) ;
2022-05-05 15:08:47 +00:00
2022-04-28 00:08:57 +00:00
await wrapper . findComponent ( GlForm ) . vm . $emit ( 'submit' , createFakeEvent ( ) ) ;
} ) ;
it ( 'updates prosemirror doc with new link' , async ( ) => {
expect ( tiptapEditor . getHTML ( ) ) . toBe (
2022-08-15 15:09:53 +00:00
'<p>Download <a target="_blank" rel="noopener noreferrer nofollow" href="https://google.com" title="Search Google">PDF File</a></p>' ,
2022-04-28 00:08:57 +00:00
) ;
} ) ;
it ( 'updates the link in the bubble menu' , ( ) => {
const link = wrapper . findComponent ( GlLink ) ;
expect ( link . attributes ( ) ) . toEqual (
expect . objectContaining ( {
href : 'https://google.com' ,
'aria-label' : 'https://google.com' ,
title : 'https://google.com' ,
target : '_blank' ,
} ) ,
) ;
expect ( link . text ( ) ) . toBe ( 'https://google.com' ) ;
} ) ;
} ) ;
describe ( 'after making changes in the form and clicking cancel' , ( ) => {
beforeEach ( async ( ) => {
linkHrefInput . setValue ( 'https://google.com' ) ;
linkTitleInput . setValue ( 'Search Google' ) ;
await wrapper . findByTestId ( 'cancel-link' ) . vm . $emit ( 'click' ) ;
} ) ;
it ( 'hides the form and shows the copy/edit/remove link buttons' , ( ) => {
expectLinkButtonsToExist ( ) ;
} ) ;
it ( 'resets the form with old values of the link from prosemirror' , async ( ) => {
// click edit once again to show the form back
await wrapper . findByTestId ( 'edit-link' ) . vm . $emit ( 'click' ) ;
2022-05-05 15:08:47 +00:00
linkHrefInput = wrapper . findByTestId ( 'link-href' ) ;
linkTitleInput = wrapper . findByTestId ( 'link-title' ) ;
2022-04-28 00:08:57 +00:00
expect ( linkHrefInput . element . value ) . toBe ( 'uploads/my_file.pdf' ) ;
expect ( linkTitleInput . element . value ) . toBe ( 'Click here to download' ) ;
} ) ;
} ) ;
} ) ;
} ) ;