From 0ca9295de5536401e7181ffca2c87a6cac7c9da1 Mon Sep 17 00:00:00 2001 From: Adam DeHaven Date: Wed, 3 Jan 2024 16:50:43 -0500 Subject: [PATCH] test: add component tests --- sandbox/App.vue | 1 + src/components/MarkdownUi.spec.ts | 200 ++++++++++++++++++++++++++++++ src/composables/useMarkdownIt.ts | 2 +- 3 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 src/components/MarkdownUi.spec.ts diff --git a/sandbox/App.vue b/sandbox/App.vue index afbe89c0..e9e796c0 100644 --- a/sandbox/App.vue +++ b/sandbox/App.vue @@ -7,6 +7,7 @@ { + const actual: any = await vi.importActual('@vueuse/core') + return { + ...actual, + useScroll: vi.fn(() => ({ + arrivedState: { + left: false, + right: false, + top: false, + bottom: false, + }, + })), + } +}) + +// Default markdown content +const defaultText = 'Markdown Content' +const defaultContent = `# ${defaultText}` + +// The markdown content takes roughly 350ms to initialize, so wait in each test. +// Not ideal. +const waitForContent = async (timeout = 350): Promise => await new Promise((resolve) => setTimeout(resolve, timeout)) + +// Stub the return value for useMediaQuery to determine if browser is at least phablet width +const mediaQuerySpy = ({ + isPhabletWidth = true, +}: { + isPhabletWidth?: boolean, + theme?: Theme, +}) => vi.spyOn(global.window, 'matchMedia').mockImplementation((query: string): MediaQueryList => { + let matches = false + if (query.includes(`min-width: ${KUI_BREAKPOINT_PHABLET}`)) { + matches = isPhabletWidth + } + + // @ts-ignore + return { + matches, + media: query, + addListener: () => {}, // Mocking addListener method + removeListener: () => {}, // Mocking removeListener method + } +}) + +describe('', () => { + beforeEach(() => { + mediaQuerySpy({}) + }) + + describe('modes', () => { + describe('read', () => { + it('displays markdown content in read mode', async () => { + const wrapper = mount(MarkdownUi, { + props: { + mode: 'read', + editable: false, + modelValue: defaultContent, + }, + }) + + await waitForContent() + + expect(wrapper.findTestId('markdown-ui').isVisible()).toBe(true) + expect(wrapper.findTestId('markdown-content').isVisible()).toBe(true) + expect(wrapper.findTestId('markdown-content').text()).toContain(defaultText) + // Elements should not exist + expect(wrapper.findTestId('edit').exists()).toBe(false) + expect(wrapper.findTestId('toolbar').exists()).toBe(false) + }) + + it('displays the Edit button read mode when `editable` is true', async () => { + const wrapper = mount(MarkdownUi, { + props: { + mode: 'read', + editable: true, + modelValue: defaultContent, + }, + }) + + await waitForContent() + + expect(wrapper.findTestId('markdown-ui').isVisible()).toBe(true) + expect(wrapper.findTestId('markdown-content').isVisible()).toBe(true) + expect(wrapper.findTestId('markdown-content').text()).toContain(defaultText) + // Edit button should be visible + expect(wrapper.findTestId('edit').isVisible()).toBe(true) + // Elements should not exist + expect(wrapper.findTestId('toolbar').exists()).toBe(false) + }) + }) + + describe('edit', () => { + it('does not show edit mode when `editable` prop is false', async () => { + const wrapper = mount(MarkdownUi, { + props: { + mode: 'edit', + editable: false, + modelValue: defaultText, + }, + }) + + // No need to wait in edit mode + await waitForContent() + + expect(wrapper.findTestId('markdown-ui').isVisible()).toBe(true) + expect(wrapper.findTestId('markdown-content').isVisible()).toBe(true) + expect(wrapper.findTestId('markdown-content').text()).toContain(defaultText) + // Elements should not exist + expect(wrapper.findTestId('edit').exists()).toBe(false) + expect(wrapper.findTestId('toolbar').exists()).toBe(false) + }) + + it('updates the v-model when the editor content is changed', async () => { + const text = '# Starter content' + const newText = '# This is the new content' + const wrapper = mount(MarkdownUi, { + props: { + mode: 'edit', + editable: true, + modelValue: text, + 'onUpdate:modelValue': (e: any) => wrapper.setProps({ modelValue: e }), + }, + }) + + // No need to wait in edit mode + await waitForContent() + + expect(wrapper.findTestId('markdown-ui').isVisible()).toBe(true) + expect(wrapper.findTestId('toolbar').isVisible()).toBe(true) + expect(wrapper.findTestId('markdown-editor-textarea').isVisible()).toBe(true) + // Expect original text + expect((wrapper.findTestId('markdown-editor-textarea').element as HTMLTextAreaElement).value).toEqual(text) + // Elements should not exist + expect(wrapper.findTestId('markdown-content').exists()).toBe(false) + expect(wrapper.findTestId('edit').exists()).toBe(false) + + // Set the new text + await wrapper.findTestId('markdown-editor-textarea').setValue(newText) + // Expect new text + expect((wrapper.findTestId('markdown-editor-textarea').element as HTMLTextAreaElement).value).toEqual(newText) + }) + }) + + // describe('split', () => { + + // }) + + // describe('preview', () => { + + // }) + }) + + describe('actions', () => { + it('copies code block text to the clipboard via the copy button', async () => { + const copiedText = ref('') + // Stub the navigator.clipboard.writeText method + window.navigator = { + // @ts-ignore + clipboard: { + writeText: vi.fn(async (text: string) => { + copiedText.value = text + }), + }, + } + + const codeContent = "const name: string = 'Marty McFly'" + const wrapper = mount(MarkdownUi, { + props: { + mode: 'read', + editable: false, + modelValue: '```typescript\n' + codeContent + '\n```', + }, + }) + + await waitForContent() + + expect(wrapper.findTestId('markdown-ui').isVisible()).toBe(true) + expect(wrapper.findTestId('markdown-content').isVisible()).toBe(true) + expect(wrapper.findTestId('markdown-content').text()).toContain(codeContent) + // Copy button should be visible + expect(wrapper.findTestId('copy-code-button').isVisible()).toBe(true) + // Ensure the copy text is empty before clicking the copy button + expect(copiedText.value).toEqual('') + + await wrapper.findTestId('copy-code-button').trigger('click') + + // Verify the text was copied + expect(copiedText.value).toEqual(codeContent) + }) + }) +}) diff --git a/src/composables/useMarkdownIt.ts b/src/composables/useMarkdownIt.ts index c58e02ed..b077ae03 100644 --- a/src/composables/useMarkdownIt.ts +++ b/src/composables/useMarkdownIt.ts @@ -119,7 +119,7 @@ export default function useMarkdownIt() { return `
${originalContent} -