From 46fc552de12fa7dfb054cb5b35c862946443c547 Mon Sep 17 00:00:00 2001 From: Eyo Okon Eyo Date: Wed, 23 Oct 2024 17:27:03 +0200 Subject: [PATCH] add tests for copying object link --- .../tabs/link/link_content.test.tsx | 191 ++++++++++++++++++ .../components/tabs/link/link_content.tsx | 17 +- 2 files changed, 200 insertions(+), 8 deletions(-) create mode 100644 src/plugins/share/public/components/tabs/link/link_content.test.tsx diff --git a/src/plugins/share/public/components/tabs/link/link_content.test.tsx b/src/plugins/share/public/components/tabs/link/link_content.test.tsx new file mode 100644 index 0000000000000..77fac4afc8ce0 --- /dev/null +++ b/src/plugins/share/public/components/tabs/link/link_content.test.tsx @@ -0,0 +1,191 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { type ComponentProps } from 'react'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import userEvent from '@testing-library/user-event'; +import { render, screen, waitFor } from '@testing-library/react'; + +import { urlServiceTestSetup } from '../../../../common/url_service/__tests__/setup'; +import { MockLocatorDefinition } from '../../../../common/url_service/mocks'; +import { BrowserShortUrlClientFactory } from '../../../url_service/short_urls/short_url_client_factory'; +import { + BrowserShortUrlClientHttp, + BrowserShortUrlClient, +} from '../../../url_service/short_urls/short_url_client'; +import { BrowserUrlService } from '../../../types'; +import { LinkContent } from './link_content'; + +const renderComponent = (props: ComponentProps) => { + render( + + + + ); +}; + +describe('LinkContent', () => { + const shareableUrl = 'http://localhost:5601/app/dashboards#/view/123'; + + const http: BrowserShortUrlClientHttp = { + basePath: { + get: () => '/xyz', + }, + fetch: jest.fn(async () => { + return {} as any; + }), + }; + + let urlService: BrowserUrlService; + + // @ts-expect-error there is a type because we override the shortUrls implementation + // eslint-disable-next-line prefer-const + ({ service: urlService } = urlServiceTestSetup({ + shortUrls: ({ locators }) => + new BrowserShortUrlClientFactory({ + http, + locators, + }), + })); + + beforeAll(() => { + Object.defineProperty(document, 'execCommand', { + value: jest.fn(() => true), + }); + }); + + it('uses the delegatedShareUrlHandler to generate the shareable URL when it is provided', async () => { + const user = userEvent.setup(); + const objectType = 'dashboard'; + const objectId = '123'; + const isDirty = false; + + const delegatedShareUrlHandler = jest.fn(); + + renderComponent({ + objectType, + objectId, + isDirty, + shareableUrl, + urlService, + allowShortUrl: true, + delegatedShareUrlHandler, + }); + + await user.click(screen.getByTestId('copyShareUrlButton')); + + expect(delegatedShareUrlHandler).toHaveBeenCalled(); + }); + + it('returns the shareable URL when the delegatedShareUrlHandler is not provided and shortURLs are not allowed', async () => { + const user = userEvent.setup(); + const objectType = 'dashboard'; + const objectId = '123'; + const isDirty = false; + + renderComponent({ + objectType, + objectId, + isDirty, + shareableUrl, + urlService, + allowShortUrl: false, + }); + + const copyButton = screen.getByTestId('copyShareUrlButton'); + + await user.click(copyButton); + + waitFor(() => { + expect(copyButton.getAttribute('data-share-url')).toBe(shareableUrl); + }); + }); + + it('invokes the createWithLocator method on the shortURL service if a locator is present when the copy button is clicked', async () => { + const user = userEvent.setup(); + const objectType = 'dashboard'; + const objectId = '123'; + const isDirty = false; + const shareableUrlLocatorParams = { + locator: new MockLocatorDefinition('TEST_LOCATOR'), + params: {}, + }; + + const shortURL = 'http://localhost:5601/xyz/r/s/yellow-orange-tomato'; + + const createWithLocatorSpy = jest.spyOn(BrowserShortUrlClient.prototype, 'createWithLocator'); + + createWithLocatorSpy.mockResolvedValue({ + // @ts-expect-error we only return locator property, as that's all we need for this test + locator: { + getUrl: jest.fn(() => Promise.resolve(shortURL)), + }, + }); + + renderComponent({ + objectType, + objectId, + isDirty, + shareableUrl, + urlService, + allowShortUrl: true, + // @ts-ignore this locator is passed mainly to test the code path that invokes createWithLocator + shareableUrlLocatorParams, + }); + + const copyButton = screen.getByTestId('copyShareUrlButton'); + + const numberOfClicks = 4; + + for (const _click of Array.from({ length: numberOfClicks })) { + await user.click(copyButton); + } + + // should only invoke once no matter how many times the button is clicked + expect(createWithLocatorSpy).toHaveBeenCalledTimes(1); + expect(copyButton.getAttribute('data-share-url')).toBe(shortURL); + }); + + it('invokes the createFromLongUrl method on the shortURL service if a locator is not present when the copy button is clicked', async () => { + const user = userEvent.setup(); + const objectType = 'dashboard'; + const objectId = '123'; + const isDirty = false; + + const shortURL = 'http://localhost:5601/xyz/r/s/yellow-orange-tomato'; + + const createFromLongUrlSpy = jest.spyOn(BrowserShortUrlClient.prototype, 'createFromLongUrl'); + + // @ts-expect-error we only return url property, as that's all we need for this test + createFromLongUrlSpy.mockResolvedValue({ + url: shortURL, + }); + + renderComponent({ + objectType, + objectId, + isDirty, + shareableUrl, + urlService, + allowShortUrl: true, + }); + + const copyButton = screen.getByTestId('copyShareUrlButton'); + + const numberOfClicks = 4; + + for (const _click of Array.from({ length: numberOfClicks })) { + await user.click(copyButton); + } + + // should only invoke once no matter how many times the button is clicked + expect(createFromLongUrlSpy).toHaveBeenCalledTimes(1); + expect(copyButton.getAttribute('data-share-url')).toBe(shortURL); + }); +}); diff --git a/src/plugins/share/public/components/tabs/link/link_content.tsx b/src/plugins/share/public/components/tabs/link/link_content.tsx index aacb294160937..40068732b3715 100644 --- a/src/plugins/share/public/components/tabs/link/link_content.tsx +++ b/src/plugins/share/public/components/tabs/link/link_content.tsx @@ -50,7 +50,7 @@ export const LinkContent = ({ allowShortUrl, delegatedShareUrlHandler, }: LinkProps) => { - const [url, setUrl] = useState(''); + const [snapshotUrl, setSnapshotUrl] = useState(''); const [isTextCopied, setTextCopied] = useState(false); const [isLoading, setIsLoading] = useState(false); const urlParamsRef = useRef(undefined); @@ -76,18 +76,19 @@ export const LinkContent = ({ }, []); useEffect(() => { - setUrl(getUrlWithUpdatedParams(shareableUrl || window.location.href)); + setSnapshotUrl(getUrlWithUpdatedParams(shareableUrl || window.location.href)); }, [getUrlWithUpdatedParams, shareableUrl]); const createShortUrl = useCallback(async () => { + const shortUrlService = urlService.shortUrls.get(null); + if (shareableUrlLocatorParams) { - const shortUrls = urlService.shortUrls.get(null); - const shortUrl = await shortUrls.createWithLocator(shareableUrlLocatorParams); + const shortUrl = await shortUrlService.createWithLocator(shareableUrlLocatorParams); return shortUrl.locator.getUrl(shortUrl.params, { absolute: true }); } else { - return (await urlService.shortUrls.get(null).createFromLongUrl(url)).url; + return (await shortUrlService.createFromLongUrl(snapshotUrl)).url; } - }, [shareableUrlLocatorParams, urlService.shortUrls, url]); + }, [shareableUrlLocatorParams, urlService.shortUrls, snapshotUrl]); const copyUrlHelper = useCallback(async () => { setIsLoading(true); @@ -97,7 +98,7 @@ export const LinkContent = ({ ? delegatedShareUrlHandler() : allowShortUrl ? await createShortUrl() - : url; + : snapshotUrl; } copyToClipboard(urlToCopy.current); @@ -113,7 +114,7 @@ export const LinkContent = ({ return true; }); setIsLoading(false); - }, [url, delegatedShareUrlHandler, allowShortUrl, createShortUrl]); + }, [snapshotUrl, delegatedShareUrlHandler, allowShortUrl, createShortUrl]); return ( <>