@silexlabs/grapesjs-advanced-selector
Version:
A GrapesJS plugin for managing advanced CSS selectors with a visual interface
182 lines (166 loc) • 11 kB
text/typescript
import {
AttributeSelector,
ClassSelector,
CustomTagSelector,
IdSelector,
SimpleSelector,
SimpleSelectorType,
TAGS,
TagSelector,
UniversalSelector,
isSameSelector,
toString,
getDisplayType,
getDisplayName,
validate,
suggest,
getCreationSuggestions,
getEditableName
} from './SimpleSelector'
describe('SimpleSelector', () => {
test('isSameSelector should return true for same selectors', () => {
const selectorA: SimpleSelector = { type: SimpleSelectorType.CLASS, value: 'test' } as ClassSelector
const selectorB: SimpleSelector = { type: SimpleSelectorType.CLASS, value: 'test' } as ClassSelector
expect(isSameSelector(selectorA, selectorB)).toBe(true)
})
test('isSameSelector should return false for different selectors', () => {
const selectorRef: SimpleSelector = { type: SimpleSelectorType.CLASS, value: 'test' } as ClassSelector
expect(isSameSelector(selectorRef, { type: SimpleSelectorType.CLASS, value: 'test2' } as ClassSelector)).toBe(false)
expect(isSameSelector(selectorRef, { type: SimpleSelectorType.ID, value: 'test' } as IdSelector)).toBe(false)
expect(isSameSelector(selectorRef, { type: SimpleSelectorType.UNIVERSAL } as UniversalSelector)).toBe(false)
expect(isSameSelector({ type: SimpleSelectorType.ATTRIBUTE, value: 'test', operator: undefined, attributeValue: 'test2' } as AttributeSelector, { type: SimpleSelectorType.ATTRIBUTE, value: 'test' } as AttributeSelector)).toBe(true)
expect(isSameSelector({ type: SimpleSelectorType.ATTRIBUTE, value: 'test', operator: '=', attributeValue: 'test2' } as AttributeSelector, { type: SimpleSelectorType.ATTRIBUTE, value: 'test' } as AttributeSelector)).toBe(false)
// Active is irrelevant
expect(isSameSelector({ type: SimpleSelectorType.CLASS, value: 'test', active: true } as ClassSelector, { type: SimpleSelectorType.CLASS, value: 'test' } as ClassSelector)).toBe(true)
expect(isSameSelector({ type: SimpleSelectorType.CLASS, value: 'test', active: true } as ClassSelector, { type: SimpleSelectorType.CLASS, value: 'test', active: false } as ClassSelector)).toBe(true)
})
test('toString should return correct string representation', () => {
const active = true
const value = 'test'
expect(toString({ type: SimpleSelectorType.CLASS, value, active } as ClassSelector)).toBe('.test')
expect(toString({ type: SimpleSelectorType.ID, value, active } as IdSelector)).toBe('#test')
expect(toString({ type: SimpleSelectorType.UNIVERSAL, active } as UniversalSelector)).toBe('*')
expect(toString({ type: SimpleSelectorType.TAG, value: TAGS[0], active } as TagSelector)).toBe(TAGS[0])
expect(toString({ type: SimpleSelectorType.CUSTOM_TAG, value: 'the-component', active } as CustomTagSelector)).toBe('the-component')
expect(toString({ type: SimpleSelectorType.ATTRIBUTE, value: 'data-test', active } as AttributeSelector)).toBe('[data-test]')
})
test('getDisplayType should return correct display type', () => {
const active = true
const value = 'test'
expect(getDisplayType({ type: SimpleSelectorType.CLASS, value, active } as ClassSelector)).toBe('•')
expect(getDisplayType({ type: SimpleSelectorType.ID, value, active } as IdSelector)).toBe('#')
expect(getDisplayType({ type: SimpleSelectorType.UNIVERSAL, active } as UniversalSelector)).toBe('')
expect(getDisplayType({ type: SimpleSelectorType.TAG, value: TAGS[0], active } as TagSelector)).toBe('⚛')
expect(getDisplayType({ type: SimpleSelectorType.CUSTOM_TAG, value: 'the-component', active } as CustomTagSelector)).toBe('⚛')
expect(getDisplayType({ type: SimpleSelectorType.ATTRIBUTE, value: 'data-test', active } as AttributeSelector)).toBe('[ ]')
})
test('getDisplayName should return correct string', () => {
const active = true
const value = 'test'
expect(getDisplayName({ type: SimpleSelectorType.CLASS, value, active } as ClassSelector)).toBe(value)
expect(getDisplayName({ type: SimpleSelectorType.ID, value, active } as IdSelector)).toBe(value)
expect(getDisplayName({ type: SimpleSelectorType.UNIVERSAL, active } as UniversalSelector)).toBe('⭐')
expect(getDisplayName({ type: SimpleSelectorType.TAG, value: TAGS[0], active } as TagSelector)).toBe(TAGS[0])
expect(getDisplayName({ type: SimpleSelectorType.CUSTOM_TAG, value: 'the-component', active } as CustomTagSelector)).toBe('the-component')
expect(getDisplayName({ type: SimpleSelectorType.ATTRIBUTE, value: 'data-test', active } as AttributeSelector)).toBe('data-test')
expect(getDisplayName({ type: SimpleSelectorType.ATTRIBUTE, value: 'data-test', operator: '=', attributeValue: 'test', active } as AttributeSelector)).toBe('data-test')
})
test('getEditableName should return the correct selector string', () => {
const active = true
const value = 'test'
expect(getEditableName({ type: SimpleSelectorType.CLASS, value, active } as ClassSelector)).toBe(`.${value}`)
expect(getEditableName({ type: SimpleSelectorType.ID, value, active } as IdSelector)).toBe(`#${value}`)
expect(getEditableName({ type: SimpleSelectorType.UNIVERSAL, active } as UniversalSelector)).toBe('*')
expect(getEditableName({ type: SimpleSelectorType.TAG, value: TAGS[0], active } as TagSelector)).toBe(TAGS[0])
expect(getEditableName({ type: SimpleSelectorType.CUSTOM_TAG, value: 'the-component', active } as CustomTagSelector)).toBe('the-component')
expect(getEditableName({ type: SimpleSelectorType.ATTRIBUTE, value: 'data-test', active } as AttributeSelector)).toBe('[data-test]')
expect(getEditableName({ type: SimpleSelectorType.ATTRIBUTE, value: 'data-test', operator: '=', attributeValue: 'test', active } as AttributeSelector)).toBe('[data-test]')
})
test('toString should return the full selector string', () => {
const active = true
const value = 'test'
expect(toString({ type: SimpleSelectorType.CLASS, value, active } as ClassSelector)).toBe(`.${value}`)
expect(toString({ type: SimpleSelectorType.ID, value, active } as IdSelector)).toBe(`#${value}`)
expect(toString({ type: SimpleSelectorType.UNIVERSAL, active } as UniversalSelector)).toBe('*')
expect(toString({ type: SimpleSelectorType.TAG, value: TAGS[0], active } as TagSelector)).toBe(TAGS[0])
expect(toString({ type: SimpleSelectorType.CUSTOM_TAG, value: 'the-component', active } as CustomTagSelector)).toBe('the-component')
expect(toString({ type: SimpleSelectorType.ATTRIBUTE, value: 'data-test', active } as AttributeSelector)).toBe('[data-test]')
expect(toString({ type: SimpleSelectorType.ATTRIBUTE, value: 'data-test', operator: '=', attributeValue: 'test', active } as AttributeSelector)).toBe('[data-test="test"]')
})
test('validate should return false for invalid value, and the fixed value otherwise', () => {
expect(validate('1invalid')).toBe(false)
expect(validate('')).toBe('')
expect(validate(' ')).toBe(false) // starting with space
expect(validate('-')).toBe(false)
expect(validate('*')).toBe('*')
expect(validate('the')).toBe('.the')
expect(validate('the-')).toBe(false)
expect(validate('the-component')).toBe('.the-component')
expect(validate('almost -valid')).toBe('.almost--valid')
expect(validate('9invalid')).toBe(false)
expect(validate('invalid ')).toBe(false)
expect(validate('inva lid')).toBe('.inva-lid')
expect(validate('data-')).toBe(false)
expect(validate('data-test')).toBe('.data-test')
expect(validate('div')).toBe('div')
expect(validate('a')).toBe('a')
expect(validate('A')).toBe('a') // 'a' is an HTML tag, lowercase by spec
// Classes and IDs preserve case (case-sensitive by CSS spec)
expect(validate('testMajuscule')).toBe('.testMajuscule')
expect(validate('.testMajuscule')).toBe('.testMajuscule')
expect(validate('#testMajuscule')).toBe('#testMajuscule')
expect(validate('.')).toBe(false)
expect(validate('.valid')).toBe('.valid')
expect(validate('.almost -valid')).toBe('.almost--valid')
expect(validate('#')).toBe(false)
expect(validate('#valid')).toBe('#valid')
expect(validate('#almost -valid')).toBe('#almost--valid')
expect(validate('[')).toBe(false)
expect(validate('[placeholder]')).toBe('[placeholder]')
expect(validate('[test')).toBe(false)
expect(validate('[test]')).toBe(false)
expect(validate('[data-')).toBe(false)
expect(validate('[data-test]')).toBe('[data-test]')
expect(validate('[data-test')).toBe('[data-test]')
expect(validate('[href="#')).toBe('[href="#"]')
expect(validate('[href="#')).toBe('[href="#"]')
expect(validate('[href="#"]')).toBe('[href="#"]')
expect(validate('body')).toBe('body')
expect(validate('html')).toBe('html')
expect(validate('div')).toBe('div')
// define a custom tag
const customTag = 'the-component'
expect(validate(customTag)).toBe(`.${customTag}`) // Not a custom element yet
window.customElements.define(customTag, class extends HTMLElement {})
expect(validate(customTag)).toBe(customTag) // Now it is a custom element
})
test('suggest should return creation suggestions for empty filter or *', () => {
//expect(suggest('', []).length).toBeGreaterThan(0)
//expect(suggest('', [])[0].createValue).toBe('.')
expect(suggest('*', [])).toHaveLength(0)
})
test('suggest should return filtered suggestions', () => {
const selector: SimpleSelector = { type: SimpleSelectorType.CLASS, value: 'test' } as ClassSelector
const suggestions = suggest('test', [selector])
expect(suggestions.length).toBe(1)
expect(suggestions[0].type).toBe(SimpleSelectorType.CLASS)
})
test('getCreationSuggestions', () => {
expect(getCreationSuggestions('.test')[0].type).toBe(SimpleSelectorType.CLASS)
expect(getCreationSuggestions('#test')).toEqual([]) // The IDs exist so they will be suggested
expect(getCreationSuggestions('div')).toEqual([]) // The tag exists so it will be suggested
expect(getCreationSuggestions('[data-test]')[0].type).toBe(SimpleSelectorType.ATTRIBUTE)
expect(getCreationSuggestions('[data-test]')[0].type).toBe(SimpleSelectorType.ATTRIBUTE)
expect(getCreationSuggestions('*')[0].type).toBe(SimpleSelectorType.UNIVERSAL)
const suggestAttr = getCreationSuggestions('[data-test="test value"]')
expect(suggestAttr).toHaveLength(1)
expect((suggestAttr[0] as AttributeSelector).type).toBe(SimpleSelectorType.ATTRIBUTE)
expect((suggestAttr[0] as AttributeSelector).value).toEqual('data-test')
expect((suggestAttr[0] as AttributeSelector).attributeValue).toEqual('test value')
expect(getCreationSuggestions('test')).toHaveLength(0)
expect(getCreationSuggestions('.test')).toHaveLength(1)
expect(getCreationSuggestions('.test')[0].type).toBe(SimpleSelectorType.CLASS)
expect((getCreationSuggestions('.test')[0] as ClassSelector).value).toBe('test')
expect(getCreationSuggestions(false)).toHaveLength(0)
})
})