import { afterEach, describe, expect, it } from 'vitest' import { mediaFileName, slugFileStem } from '../../assets/src/blogeditor/imageform' import { isExternalUrl } from '../../assets/src/blogeditor/linkform' import { button, chooseImage, click, fill, form, mount } from './helpers' // J3 link form and J4 image form (UI-SPEC J3, J4): the rel checkboxes // follow the URL as it is typed, the generic-text warning comes and goes // without blocking, the file-name slug rule's examples, and a decorative // image clears and locks its alt. The first cases are in additions.test.ts. afterEach(() => { document.body.replaceChildren() }) const ON = JSON.stringify({ headingLevels: [2, 3, 4], genericAnchors: ['click here', 'czytaj więcej'] }) function openLink(text: string) { const m = mount(`

${text}

`, { options: ON }) m.editor.commands.setTextSelection({ from: 1, to: 1 + text.length }) click(button(m.el, 'link')) const linkForm = form(m.el, 'link')! const box = (token: string) => linkForm.querySelector(`input[name="rel-${token}"]`)! return { ...m, linkForm, box, url: linkForm.querySelector('input[name="url"]')! } } describe('J3 rel checkboxes', () => { it('enable for an absolute http(s) URL and disable again for an internal one', () => { const { box, url } = openLink('Partner') for (const token of ['nofollow', 'sponsored', 'ugc']) expect(box(token).disabled, token).toBe(true) fill(url, 'https://partner.test') for (const token of ['nofollow', 'sponsored', 'ugc']) expect(box(token).disabled, token).toBe(false) box('ugc').checked = true fill(url, '/blog/partner') expect(box('ugc').disabled).toBe(true) }) it('writes only the checked tokens, in order, for an external link', () => { const { el, box, url, linkForm } = openLink('Partner') fill(url, 'https://partner.test/x') box('ugc').checked = true box('nofollow').checked = true click(linkForm.querySelector('[data-action="apply"]')) expect(el.value).toMatch(/^

Partner<\/a><\/p>$/) }) it('writes no rel for an internal link even when a box was checked', () => { const { el, box, url, linkForm } = openLink('Partner') fill(url, 'https://partner.test') box('sponsored').checked = true fill(url, '/oferta') click(linkForm.querySelector('[data-action="apply"]')) expect(el.value).toBe('

Partner

') }) }) describe('J3 generic link text', () => { it('warns for a listed text in any case or punctuation and stays quiet otherwise', () => { const generic = openLink('Czytaj więcej…') expect(generic.linkForm.querySelector('[data-warning]')!.hidden).toBe(false) fill(generic.url, '/blog/a') click(generic.linkForm.querySelector('[data-action="apply"]')) expect(generic.el.value).toBe('

Czytaj więcej…

') document.body.replaceChildren() const specific = openLink('Badania nad chagą') expect(specific.linkForm.querySelector('[data-warning]')!.hidden).toBe(true) }) it('reads external URLs by scheme only', () => { for (const [u, want] of [ ['https://a.test/x?y=1', true], ['http://a.test', true], ['/kontakt', false], ['./a', false], ['ftp://a.test', false], ['javascript:alert(1)', false], ['', false], ] as const) { expect(isExternalUrl(u), u).toBe(want) } }) }) describe('J4 file names', () => { it('follows the slug rule examples', () => { for (const [name, want] of [ ['Chaga – zdjęcie 1.JPEG', 'chaga-zdjecie-1.jpeg'], [' Kurki w koszyku .png', 'kurki-w-koszyku.png'], ['IMG_2024.06.01.webp', 'img-2024-06-01.webp'], ['___.gif', 'image.gif'], ['bez-rozszerzenia', 'bez-rozszerzenia'], ] as const) { expect(mediaFileName(name), name).toBe(want) } expect(slugFileStem('Łódź & Kraków')).toBe('lodz-krakow') expect(slugFileStem('--a--b--')).toBe('a-b') }) it('clears and locks the alt of a decorative image and frees it again', () => { const { el } = mount('', { options: ON }) const input = el.querySelector('input[type="file"]')! Object.defineProperty(input, 'files', { value: [new File([new Uint8Array([137, 80, 78, 71])], 'linia.png', { type: 'image/png' })], configurable: true }) input.dispatchEvent(new Event('change')) const imageForm = form(el, 'image')! const alt = imageForm.querySelector('input[name="alt"]')! const decorative = imageForm.querySelector('input[name="decorative"]')! fill(alt, 'Linia') decorative.checked = true decorative.dispatchEvent(new Event('change')) expect(alt.value).toBe('') expect(alt.disabled).toBe(true) decorative.checked = false decorative.dispatchEvent(new Event('change')) expect(alt.disabled).toBe(false) chooseImage(el, { alt: 'Linia' }) }) })