import { Application } from '@hotwired/stimulus'; import { SlugController } from './SlugController'; describe('SlugController', () => { let application; beforeEach(() => { application?.stop(); document.body.innerHTML = ` `; application = Application.start(); application.register('w-slug', SlugController); }); it('should trim and slugify the input value when focus is moved away from it', () => { const slugInput = document.querySelector('#id_slug'); slugInput.value = ' slug testing on edit page '; slugInput.dispatchEvent(new CustomEvent('blur')); expect(document.querySelector('#id_slug').value).toEqual( 'slug-testing-on-edit-page', ); }); it('should not allow unicode characters by default', () => { const slugInput = document.querySelector('#id_slug'); expect( slugInput.hasAttribute('data-w-slug-allow-unicode-value'), ).toBeFalsy(); slugInput.value = 'Visiter Toulouse en été 2025'; slugInput.dispatchEvent(new CustomEvent('blur')); expect(slugInput.value).toEqual('visiter-toulouse-en-t-2025'); }); it('should now allow unicode characters by default', () => { const slugInput = document.querySelector('#id_slug'); slugInput.setAttribute('data-w-slug-allow-unicode-value', 'true'); expect( slugInput.hasAttribute('data-w-slug-allow-unicode-value'), ).toBeTruthy(); slugInput.value = 'Visiter Toulouse en été 2025'; slugInput.dispatchEvent(new CustomEvent('blur')); expect(slugInput.value).toEqual('visiter-toulouse-en-été-2025'); }); }); describe('compare behaviour', () => { let application; beforeEach(() => { application?.stop(); document.body.innerHTML = ` `; application = Application.start(); application.register('w-slug', SlugController); const slugInput = document.querySelector('#id_slug'); slugInput.dataset.action = [ 'blur->w-slug#urlify', 'custom:event->w-slug#compare', ].join(' '); }); it('should not prevent default if input has no value', () => { const event = new CustomEvent('custom:event', { detail: { value: 'title alpha' }, }); event.preventDefault = jest.fn(); document.getElementById('id_slug').dispatchEvent(event); expect(document.getElementById('id_slug').value).toBe(''); expect(event.preventDefault).not.toHaveBeenCalled(); }); it('should not prevent default if the values are the same', () => { document.querySelector('#id_slug').setAttribute('value', 'title-alpha'); const event = new CustomEvent('custom:event', { detail: { value: 'title alpha' }, }); event.preventDefault = jest.fn(); document.getElementById('id_slug').dispatchEvent(event); expect(event.preventDefault).not.toHaveBeenCalled(); }); it('should prevent default using the slugify (default) behaviour as the compare function when urlify values is not equal', () => { const slug = document.querySelector('#id_slug'); const title = 'Тестовий заголовок'; slug.setAttribute('value', title); // apply the urlify method to the content to ensure the value before check is urlify slug.dispatchEvent(new Event('blur')); expect(slug.value).toEqual('testovij-zagolovok'); const event = new CustomEvent('custom:event', { detail: { value: title } }); event.preventDefault = jest.fn(); slug.dispatchEvent(event); // slugify used for the compareAs value by default, so 'compare' fails expect(event.preventDefault).toHaveBeenCalled(); }); it('should not prevent default using the slugify (default) behaviour as the compare function when urlify value is equal', () => { const slug = document.querySelector('#id_slug'); const title = 'the-french-dispatch-a-love-letter-to-journalists'; slug.setAttribute('value', title); // apply the urlify method to the content to ensure the value before check is urlify slug.dispatchEvent(new Event('blur')); expect(slug.value).toEqual( 'the-french-dispatch-a-love-letter-to-journalists', ); const event = new CustomEvent('custom:event', { detail: { value: title } }); event.preventDefault = jest.fn(); slug.dispatchEvent(event); // slugify used for the compareAs value by default, so 'compare' passes with the initial urlify value on blur expect(event.preventDefault).not.toHaveBeenCalled(); }); it('should not prevent default using the urlify behaviour as the compare function when urlify value matches', () => { const title = 'Тестовий заголовок'; const slug = document.querySelector('#id_slug'); slug.setAttribute('data-w-slug-compare-as-param', 'urlify'); slug.setAttribute('value', title); // apply the urlify method to the content to ensure the value before check is urlify slug.dispatchEvent(new Event('blur')); expect(slug.value).toEqual('testovij-zagolovok'); const event = new CustomEvent('custom:event', { detail: { compareAs: 'urlify', value: title }, }); event.preventDefault = jest.fn(); slug.dispatchEvent(event); expect(event.preventDefault).not.toHaveBeenCalled(); }); it('should prevent default if the values are not the same', () => { document.querySelector('#id_slug').setAttribute('value', 'title-alpha'); const event = new CustomEvent('custom:event', { detail: { value: 'title beta' }, }); event.preventDefault = jest.fn(); document.getElementById('id_slug').dispatchEvent(event); expect(event.preventDefault).toHaveBeenCalled(); }); it('should not prevent default if both values are empty strings', () => { const slugInput = document.querySelector('#id_slug'); slugInput.setAttribute('value', ''); const event = new CustomEvent('custom:event', { detail: { value: '' }, }); event.preventDefault = jest.fn(); slugInput.dispatchEvent(event); expect(event.preventDefault).not.toHaveBeenCalled(); }); it('should prevent default if the new value is an empty string but the existing value is not', () => { const slugInput = document.querySelector('#id_slug'); slugInput.setAttribute('value', 'existing-value'); const event = new CustomEvent('custom:event', { detail: { value: '' }, }); event.preventDefault = jest.fn(); slugInput.dispatchEvent(event); expect(event.preventDefault).toHaveBeenCalled(); }); }); describe('urlify behaviour', () => { let application; beforeEach(() => { application?.stop(); document.body.innerHTML = ` `; application = Application.start(); application.register('w-slug', SlugController); const slugInput = document.querySelector('#id_slug'); slugInput.dataset.action = [ 'blur->w-slug#slugify', 'custom:event->w-slug#urlify:prevent', ].join(' '); }); it('should update slug input value if the values are the same', () => { const slugInput = document.getElementById('id_slug'); slugInput.value = 'urlify Testing On edit page '; const event = new CustomEvent('custom:event', { detail: { value: 'urlify Testing On edit page' }, bubbles: false, }); document.getElementById('id_slug').dispatchEvent(event); expect(slugInput.value).toBe('urlify-testing-on-edit-page'); }); it('should transform input with special characters to their ASCII equivalent', () => { const slugInput = document.getElementById('id_slug'); slugInput.value = 'Some Title with éçà Spaces'; const event = new CustomEvent('custom:event', { detail: { value: 'Some Title with éçà Spaces' }, }); document.getElementById('id_slug').dispatchEvent(event); expect(slugInput.value).toBe('some-title-with-eca-spaces'); }); it('should return an empty string when input contains only special characters', () => { const slugInput = document.getElementById('id_slug'); slugInput.value = '$$!@#$%^&*'; const event = new CustomEvent('custom:event', { detail: { value: '$$!@#$%^&*' }, }); document.getElementById('id_slug').dispatchEvent(event); expect(slugInput.value).toBe(''); }); });