diff --git a/superset-frontend/src/utils/navigationUtils.AppLink.test.tsx b/superset-frontend/src/utils/navigationUtils.AppLink.test.tsx new file mode 100644 index 00000000000..8644efe01d7 --- /dev/null +++ b/superset-frontend/src/utils/navigationUtils.AppLink.test.tsx @@ -0,0 +1,69 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { render } from 'spec/helpers/testing-library'; +import { AppLink } from 'src/utils/navigationUtils'; + +// AppLink renders a real React element via React Testing Library, which is +// incompatible with the withApplicationRoot fixture's `jest.resetModules()` +// (it corrupts the testing-library module graph). Mock applicationRoot at +// file scope and vary per test instead. Variable must start with `mock` to +// satisfy Jest's hoisted-factory out-of-scope check. +const mockApplicationRoot = jest.fn(() => ''); + +jest.mock('src/utils/getBootstrapData', () => ({ + __esModule: true, + default: () => ({ + common: { application_root: '', static_assets_prefix: '' }, + }), + applicationRoot: () => mockApplicationRoot(), + staticAssetsPrefix: () => '', +})); + +beforeEach(() => { + mockApplicationRoot.mockReturnValue(''); +}); + +test('renders an anchor with prefixed href under subdirectory deployment', () => { + mockApplicationRoot.mockReturnValue('/superset'); + const { container } = render(go); + const anchor = container.querySelector('a'); + expect(anchor).not.toBeNull(); + expect(anchor?.getAttribute('href')).toBe('/superset/foo'); +}); + +test('passes through other anchor props', () => { + const { container } = render( + + go + , + ); + const anchor = container.querySelector('a'); + expect(anchor?.getAttribute('target')).toBe('_blank'); + expect(anchor?.getAttribute('rel')).toBe('noreferrer'); +}); + +test('passes absolute URLs through without prefixing', () => { + mockApplicationRoot.mockReturnValue('/superset'); + const { container } = render( + x, + ); + expect(container.querySelector('a')?.getAttribute('href')).toBe( + 'https://external.example.com', + ); +}); diff --git a/superset-frontend/src/utils/navigationUtils.test.ts b/superset-frontend/src/utils/navigationUtils.test.ts index 2051def78d0..aae326dded0 100644 --- a/superset-frontend/src/utils/navigationUtils.test.ts +++ b/superset-frontend/src/utils/navigationUtils.test.ts @@ -164,41 +164,10 @@ describe('getShareableUrl', () => { }); }); -describe('AppLink', () => { - test('renders an anchor with prefixed href under subdirectory deployment', async () => { - await withApplicationRoot('/superset/', async () => { - const { AppLink } = await import('src/utils/navigationUtils'); - const { render } = await import('@testing-library/react'); - const { container } = render(AppLink({ href: '/foo', children: 'go' })); - const anchor = container.querySelector('a'); - expect(anchor).not.toBeNull(); - expect(anchor?.getAttribute('href')).toBe('/superset/foo'); - }); - }); - - test('passes through other anchor props', async () => { - await withApplicationRoot('', async () => { - const { AppLink } = await import('src/utils/navigationUtils'); - const { render } = await import('@testing-library/react'); - const { container } = render( - AppLink({ href: '/foo', target: '_blank', rel: 'noreferrer' }), - ); - const anchor = container.querySelector('a'); - expect(anchor?.getAttribute('target')).toBe('_blank'); - expect(anchor?.getAttribute('rel')).toBe('noreferrer'); - }); - }); - - test('passes absolute URLs through without prefixing', async () => { - await withApplicationRoot('/superset/', async () => { - const { AppLink } = await import('src/utils/navigationUtils'); - const { render } = await import('@testing-library/react'); - const { container } = render( - AppLink({ href: 'https://external.example.com', children: 'x' }), - ); - expect(container.querySelector('a')?.getAttribute('href')).toBe( - 'https://external.example.com', - ); - }); - }); -}); +// AppLink renders a real React element, so its tests can't use +// withApplicationRoot — `jest.resetModules()` corrupts @testing-library/react +// when its dist files are re-imported across the reset. Mock applicationRoot +// at module scope and vary it per test instead. +// +// Note: the mock factory is hoisted, so `mockApplicationRoot` must be +// `mock`-prefixed to satisfy Jest's out-of-scope-variable check.