From 86c88ac189a254725ba75b8e823a25fb38a6eba0 Mon Sep 17 00:00:00 2001 From: Cody Jackson Date: Tue, 16 Sep 2025 10:17:27 -0700 Subject: [PATCH] Adding Preview unit tests --- .../Metadata/__tests__/KeyValue.test.ts | 2 +- .../Detail/Preview/__tests__/Content.spec.ts | 71 ++++++++++ .../Detail/Preview/__tests__/Preview.spec.ts | 121 ++++++++++++++++++ 3 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 shell/components/Resource/Detail/Preview/__tests__/Content.spec.ts create mode 100644 shell/components/Resource/Detail/Preview/__tests__/Preview.spec.ts diff --git a/shell/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts b/shell/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts index 09335bd617..feac3dfd64 100644 --- a/shell/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +++ b/shell/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts @@ -84,7 +84,7 @@ describe('component: Metadata/IdentifyingInformation', () => { expect(wrapper.find('.show-all').exists()).toBeTruthy(); }); - it('should pass type down to KeyValueRow', async () => { + it('should pass type down to KeyValueRow', async() => { const wrapper = mount(KeyValue, { props: { propertyName, rows, type: 'active' diff --git a/shell/components/Resource/Detail/Preview/__tests__/Content.spec.ts b/shell/components/Resource/Detail/Preview/__tests__/Content.spec.ts new file mode 100644 index 0000000000..89d128e999 --- /dev/null +++ b/shell/components/Resource/Detail/Preview/__tests__/Content.spec.ts @@ -0,0 +1,71 @@ +import { shallowMount } from '@vue/test-utils'; +import Content from '@shell/components/Resource/Detail/Preview/Content.vue'; +import CodeMirror from '@shell/components/CodeMirror.vue'; + +describe('component: Resource Detail Preview Content', () => { + const toTextDirective = (el: any, binding: any) => { + el.textContent = binding.value; + }; + const global = { + directives: { + cleanHtml: toTextDirective, + t: toTextDirective + }, + stubs: { CodeMirror: true } + }; + + it('should display an empty message when value is empty', () => { + const wrapper = shallowMount(Content, { + props: { value: '' }, + global + }); + + const span = wrapper.find('span'); + + expect(span.exists()).toBe(true); + expect(span.text()).toBe('detailText.empty'); + }); + + it('should display a CodeMirror component for a valid JSON string', () => { + const jsonValue = '{"key":"value"}'; + const wrapper = shallowMount(Content, { + props: { value: jsonValue }, + global + }); + + const codeMirror = wrapper.findComponent(CodeMirror); + + expect(codeMirror.exists()).toBe(true); + + const expectedFormattedJson = JSON.stringify(JSON.parse(jsonValue), null, 2); + + expect(codeMirror.props('value')).toBe(expectedFormattedJson); + }); + + it('should display a plain text message for a non-JSON string', () => { + const textValue = 'line 1\nline 2'; + const wrapper = shallowMount(Content, { + props: { value: textValue }, + global + }); + + const span = wrapper.find('[data-testid="detail-top_html"]'); + + expect(span.exists()).toBe(true); + expect(span.text()).toBe('line 1
\nline 2'); + expect(span.classes()).toContain('monospace'); + }); + + it('should display a plain text message for a string that looks like JSON but is invalid', () => { + const invalidJson = '{"key":}'; + const wrapper = shallowMount(Content, { + props: { value: invalidJson }, + global + }); + + const span = wrapper.find('[data-testid="detail-top_html"]'); + + expect(span.exists()).toBe(true); + expect(span.text()).toBe('{"key":}'); + }); +}); diff --git a/shell/components/Resource/Detail/Preview/__tests__/Preview.spec.ts b/shell/components/Resource/Detail/Preview/__tests__/Preview.spec.ts new file mode 100644 index 0000000000..67da2fc7cf --- /dev/null +++ b/shell/components/Resource/Detail/Preview/__tests__/Preview.spec.ts @@ -0,0 +1,121 @@ +import { mount } from '@vue/test-utils'; +import Preview from '@shell/components/Resource/Detail/Preview/Preview.vue'; +import { useBasicSetupFocusTrap } from '@shell/composables/focusTrap'; + +jest.mock('@shell/utils/clipboard', () => ({ copyTextToClipboard: jest.fn() })); +jest.mock('@shell/composables/focusTrap', () => ({ useBasicSetupFocusTrap: jest.fn() })); + +const teleportTarget = document.createElement('div'); + +teleportTarget.id = 'preview'; +document.body.appendChild(teleportTarget); + +describe('component: Resource Detail Preview', () => { + const global = { + stubs: { + Teleport: true, + Content: true, + CopyToClipboard: true, + }, + }; + const anchorElement = document.createElement('div'); + + // Mock getBoundingClientRect + anchorElement.getBoundingClientRect = () => ({ + x: 100, + y: 100, + width: 50, + height: 20, + top: 100, + left: 100, + right: 150, + bottom: 120, + toJSON: () => ({}), + }); + + it('should render title and pass value to children', () => { + const wrapper = mount(Preview, { + props: { + title: 'My Test Title', + value: 'My test value', + anchorElement, + }, + global + }); + + // Test title + const titleDiv = wrapper.find('.title'); + + expect(titleDiv.exists()).toBe(true); + expect(titleDiv.text()).toBe('My Test Title'); + + // Test props passed to Content + const content = wrapper.findComponent({ name: 'Content' }); + + expect(content.exists()).toBe(true); + expect(content.props('value')).toBe('My test value'); + + // Test props passed to CopyToClipboard + const copy = wrapper.findComponent({ name: 'CopyToClipboard' }); + + expect(copy.exists()).toBe(true); + expect(copy.props('value')).toBe('My test value'); + }); + + it('should emit close on focusout', async() => { + const wrapper = mount(Preview, { + props: { + title: 'Test', + value: 'Value', + anchorElement, + }, + global + }); + + const previewDiv = wrapper.find('.preview'); + + await previewDiv.trigger('focusout'); + + expect(wrapper.emitted('close')).toBeTruthy(); + expect(wrapper.emitted('close')?.[0]).toStrictEqual([false]); + }); + + it('should emit close with true on Escape keydown', async() => { + const wrapper = mount(Preview, { + props: { + title: 'Test', + value: 'Value', + anchorElement, + }, + global + }); + + const previewDiv = wrapper.find('.preview'); + + // Spy on blur to see if it's called + const blurSpy = jest.spyOn(previewDiv.element, 'blur'); + + await previewDiv.trigger('keydown.Escape'); + + expect(blurSpy).toHaveBeenCalledWith(); + + // Manually trigger focusout as jsdom doesn't do it automatically on blur() + await previewDiv.trigger('focusout'); + + expect(wrapper.emitted('close')).toBeTruthy(); + expect(wrapper.emitted('close')?.[0]).toStrictEqual([true]); + }); + + it('should call focus trap composable', () => { + mount(Preview, { + props: { + title: 'Test', + value: 'Value', + anchorElement, + }, + global + }); + + expect(useBasicSetupFocusTrap).toHaveBeenCalledWith('#focus-trap-preview-container-element'); + }); +});