WR-02 proofs and T-14.2.1-19..23 must fail the phase closed if hydration or child locale validation is removed. Co-authored-by: Cursor <cursoragent@cursor.com>
83 lines
3.1 KiB
TypeScript
83 lines
3.1 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
import { enableAutoUnmount, mount } from '@vue/test-utils'
|
|
import { ref } from 'vue'
|
|
import type { FormField } from '../../src/api/types'
|
|
import MarkdownField from '../../src/components/form/fields/MarkdownField.vue'
|
|
import MLMarkdownField from '../../src/components/form/fields/MLMarkdownField.vue'
|
|
import { FORM_ENABLED_LOCALES } from '../../src/components/form/formContext'
|
|
import { resetState } from '../helpers'
|
|
|
|
function field(type = 'markdown', name = 'body'): FormField {
|
|
return { name, label: 'Body', type } as FormField
|
|
}
|
|
|
|
beforeEach(() => {
|
|
resetState()
|
|
})
|
|
|
|
afterEach(() => {
|
|
document.body.innerHTML = ''
|
|
})
|
|
|
|
enableAutoUnmount(afterEach)
|
|
|
|
describe('MarkdownField composition and sinks', () => {
|
|
it('edits source and previews interpolated text without v-html', async () => {
|
|
const wrapper = mount(MarkdownField, {
|
|
props: {
|
|
field: field(),
|
|
modelValue: '# Hello',
|
|
controlId: 'f-body',
|
|
},
|
|
attachTo: document.body,
|
|
})
|
|
expect(wrapper.find('textarea').element.value).toBe('# Hello')
|
|
await wrapper.find('textarea').setValue('# Changed')
|
|
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['# Changed'])
|
|
await wrapper.setProps({ modelValue: '# Changed' })
|
|
await wrapper.find('[data-markdown-preview]').trigger('click')
|
|
expect(wrapper.find('[data-markdown-preview-pane]').text()).toBe('# Changed')
|
|
expect(wrapper.find('h1').exists()).toBe(false)
|
|
})
|
|
|
|
it('does not execute script, iframe, or event-handler HTML', async () => {
|
|
const wrapper = mount(MarkdownField, {
|
|
props: {
|
|
field: field(),
|
|
modelValue:
|
|
'<script>window.__md_xss = 1</script><iframe src="javascript:alert(1)"></iframe><img src=x onerror="window.__md_xss = 1">',
|
|
controlId: 'f-body',
|
|
},
|
|
attachTo: document.body,
|
|
})
|
|
await wrapper.find('[data-markdown-preview]').trigger('click')
|
|
expect(wrapper.find('script').exists()).toBe(false)
|
|
expect(wrapper.find('iframe').exists()).toBe(false)
|
|
expect(wrapper.find('img').exists()).toBe(false)
|
|
expect((window as unknown as { __md_xss?: number }).__md_xss).toBeUndefined()
|
|
expect(wrapper.html()).not.toMatch(/v-html|innerHTML/)
|
|
})
|
|
|
|
it('composes inside mlmarkdown without a raw-HTML sink', async () => {
|
|
const wrapper = mount(MLMarkdownField, {
|
|
props: {
|
|
field: field('mlmarkdown', 'body'),
|
|
modelValue: { en: '# Hi', pl: '<script>window.__md_xss = 1</script>' },
|
|
controlId: 'f-body',
|
|
},
|
|
attachTo: document.body,
|
|
global: {
|
|
provide: {
|
|
[FORM_ENABLED_LOCALES as symbol]: ref(['en', 'pl']),
|
|
},
|
|
},
|
|
})
|
|
expect(wrapper.findComponent(MarkdownField).exists()).toBe(true)
|
|
await wrapper.find('[data-ml-locale]').setValue('pl')
|
|
await wrapper.find('[data-markdown-preview]').trigger('click')
|
|
expect(wrapper.find('script').exists()).toBe(false)
|
|
expect((window as unknown as { __md_xss?: number }).__md_xss).toBeUndefined()
|
|
expect(wrapper.find('[data-markdown-preview-pane]').text()).toContain('<script>')
|
|
})
|
|
})
|