Skip to content

Commit

Permalink
test: add component tests
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdehaven committed Jan 3, 2024
1 parent c960221 commit 0ca9295
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 1 deletion.
1 change: 1 addition & 0 deletions sandbox/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<MarkdownUi
v-model="content"
:editable="editable"
mode="edit"
@cancel="cancelEdit"
@mode="modeChanged"
@save="contentSaved"
Expand Down
200 changes: 200 additions & 0 deletions src/components/MarkdownUi.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// Vitest unit test spec file

import { vi, describe, it, expect, beforeEach } from 'vitest'
import { mount } from '@vue/test-utils'
import { ref } from 'vue'
import MarkdownUi from './MarkdownUi.vue'
import { KUI_BREAKPOINT_PHABLET } from '@kong/design-tokens'
import type { Theme } from '@/types'

vi.mock('@vueuse/core', async () => {
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<void> => 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('<MarkdownUi />', () => {
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, {

Check failure on line 125 in src/components/MarkdownUi.spec.ts

View workflow job for this annotation

GitHub Actions / Tests / Run Tests

'wrapper' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
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)
})
})
})
2 changes: 1 addition & 1 deletion src/composables/useMarkdownIt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export default function useMarkdownIt() {
return `
<div class="kong-markdown-code-block-container" style="position: relative">
${originalContent}
<button class="kong-markdown-code-block-copy" data-copytext="${content}" aria-label="Copy code" tabindex="0" type="button">
<button class="kong-markdown-code-block-copy" data-copytext="${content}" aria-label="Copy code" tabindex="0" type="button" data-testid="copy-code-button">
${COPY_ICON_SVG}
</button>
</div>
Expand Down

0 comments on commit 0ca9295

Please sign in to comment.