@ordojs/core
Version:
Core compiler and runtime for OrdoJS framework
155 lines (115 loc) • 6.08 kB
text/typescript
/**
* @fileoverview Tests for the CSS Scoper
*/
import { describe, expect, it } from 'vitest';
import { OrdoJSCSSParser } from './css-parser.js';
import { OrdoJSCSSScoper } from './css-scoper.js';
describe('OrdoJSCSSScoper', () => {
it('should scope a simple CSS rule', () => {
const parser = new OrdoJSCSSParser();
const scoper = new OrdoJSCSSScoper();
const css = `.button { color: red; }`;
const styleBlock = parser.parse(css, true);
const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'ButtonComponent');
const scopedCSS = scoper.generateScopedCSS(scopedStyleBlock);
expect(scopedStyleBlock.rules[0].selector).toContain('[data-ordojs-buttoncomponent-');
expect(scopedCSS).toContain('.button[data-ordojs-buttoncomponent-');
});
it('should scope multiple selectors', () => {
const parser = new OrdoJSCSSParser();
const scoper = new OrdoJSCSSScoper();
const css = `.button, .btn { color: red; }`;
const styleBlock = parser.parse(css, true);
const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'ButtonComponent');
const scopedCSS = scoper.generateScopedCSS(scopedStyleBlock);
expect(scopedStyleBlock.rules[0].selector).toContain('.button[data-ordojs-buttoncomponent-');
expect(scopedStyleBlock.rules[0].selector).toContain('.btn[data-ordojs-buttoncomponent-');
expect(scopedCSS).toContain('.button[data-ordojs-buttoncomponent-');
expect(scopedCSS).toContain('.btn[data-ordojs-buttoncomponent-');
});
it('should handle pseudo-classes and pseudo-elements', () => {
const parser = new OrdoJSCSSParser();
const scoper = new OrdoJSCSSScoper();
const css = `
.button:hover { color: blue; }
.input::placeholder { color: gray; }
`;
const styleBlock = parser.parse(css, true);
const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'FormComponent');
expect(scopedStyleBlock.rules[0].selector).toContain('.button[data-ordojs-formcomponent-');
expect(scopedStyleBlock.rules[0].selector).toContain(':hover');
expect(scopedStyleBlock.rules[1].selector).toContain('.input[data-ordojs-formcomponent-');
expect(scopedStyleBlock.rules[1].selector).toContain('::placeholder');
});
it('should not scope global selectors like :root, html, body', () => {
const parser = new OrdoJSCSSParser();
const scoper = new OrdoJSCSSScoper();
const css = `
:root { --primary-color: blue; }
html { font-size: 16px; }
body { margin: 0; }
`;
const styleBlock = parser.parse(css, true);
const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'AppComponent');
expect(scopedStyleBlock.rules[0].selector).toBe(':root');
expect(scopedStyleBlock.rules[1].selector).toBe('html');
expect(scopedStyleBlock.rules[2].selector).toBe('body');
});
it('should handle complex selectors with combinators', () => {
const parser = new OrdoJSCSSParser();
const scoper = new OrdoJSCSSScoper();
const css = `
.parent > .child { margin: 10px; }
.sibling + .adjacent { padding: 5px; }
.list ~ .item { border: 1px solid black; }
`;
const styleBlock = parser.parse(css, true);
const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'LayoutComponent');
expect(scopedStyleBlock.rules[0].selector).toContain('.parent[data-ordojs-layoutcomponent-');
expect(scopedStyleBlock.rules[0].selector).toContain('> .child[data-ordojs-layoutcomponent-');
expect(scopedStyleBlock.rules[1].selector).toContain('.sibling[data-ordojs-layoutcomponent-');
expect(scopedStyleBlock.rules[1].selector).toContain('+ .adjacent[data-ordojs-layoutcomponent-');
expect(scopedStyleBlock.rules[2].selector).toContain('.list[data-ordojs-layoutcomponent-');
expect(scopedStyleBlock.rules[2].selector).toContain('~ .item[data-ordojs-layoutcomponent-');
});
it('should use class-based scoping when configured', () => {
const parser = new OrdoJSCSSParser();
const scoper = new OrdoJSCSSScoper({ useDataAttributes: false });
const css = `.button { color: red; }`;
const styleBlock = parser.parse(css, true);
const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'ButtonComponent');
const scopedCSS = scoper.generateScopedCSS(scopedStyleBlock);
expect(scopedStyleBlock.rules[0].selector).toContain('.button.ordoica-buttoncomponent-');
expect(scopedCSS).toContain('.button.ordoica-buttoncomponent-');
});
it('should preserve original selectors when configured', () => {
const parser = new OrdoJSCSSParser();
const scoper = new OrdoJSCSSScoper({ preserveOriginalSelectors: true });
const css = `.button { color: red; }`;
const styleBlock = parser.parse(css, true);
const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'ButtonComponent');
const scopedCSS = scoper.generateScopedCSS(scopedStyleBlock);
expect(scopedStyleBlock.rules[0].selector).toContain('.button, .button[data-ordojs-buttoncomponent-');
expect(scopedCSS).toContain('.button, .button[data-ordojs-buttoncomponent-');
});
it('should not scope styles when scoped flag is false', () => {
const parser = new OrdoJSCSSParser();
const scoper = new OrdoJSCSSScoper();
const css = `.button { color: red; }`;
const styleBlock = parser.parse(css, false); // scoped = false
const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'ButtonComponent');
expect(scopedStyleBlock.rules[0].selector).toBe('.button');
});
it('should generate consistent scope IDs for the same component', () => {
const scoper = new OrdoJSCSSScoper();
const attr1 = scoper.generateScopeAttribute('ButtonComponent');
const attr2 = scoper.generateScopeAttribute('ButtonComponent');
expect(attr1).toBe(attr2);
});
it('should generate different scope IDs for different components', () => {
const scoper = new OrdoJSCSSScoper();
const attr1 = scoper.generateScopeAttribute('ButtonComponent');
const attr2 = scoper.generateScopeAttribute('InputComponent');
expect(attr1).not.toBe(attr2);
});
});