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('');
});
});