Skip to content

Commit fdcd25a

Browse files
committed
test: add comprehensive SSR and integration tests
- Add SSR tests with jsdom (14 tests) - Add Nitro plugin integration tests (10 tests) - Add element positioning tests - Add pattern processing pipeline tests - Create test helpers and setup utilities - Configure Vitest for different test environments Authored by: Aaron Lippold<lippold@gmail.com>
1 parent cdabdca commit fdcd25a

File tree

7 files changed

+1443
-0
lines changed

7 files changed

+1443
-0
lines changed

test/helpers/setup.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* Test setup helpers for nuxt-smartscript
3+
* Provides reusable fixtures and utilities for tests
4+
*/
5+
6+
import type { SuperscriptConfig } from '../../src/runtime/smartscript/types'
7+
import { fileURLToPath } from 'node:url'
8+
import { JSDOM } from 'jsdom'
9+
import { DEFAULT_CONFIG } from '../../src/runtime/smartscript/config'
10+
11+
/**
12+
* Creates a JSDOM environment for unit/integration tests
13+
*/
14+
export function setupDOM(html = '<!DOCTYPE html><html><head></head><body></body></html>') {
15+
const dom = new JSDOM(html)
16+
const { window } = dom
17+
const { document } = window
18+
19+
// Set up globals - using any is intentional for test mocking
20+
/* eslint-disable ts/no-explicit-any */
21+
global.document = document as any
22+
global.window = window as any
23+
global.NodeFilter = window.NodeFilter
24+
global.HTMLElement = window.HTMLElement as any
25+
global.Element = window.Element as any
26+
global.Node = window.Node as any
27+
/* eslint-enable ts/no-explicit-any */
28+
29+
return { dom, document, window }
30+
}
31+
32+
/**
33+
* Clean up DOM globals after tests
34+
*/
35+
export function cleanupDOM() {
36+
// Clean up globals - using any is intentional for test cleanup
37+
/* eslint-disable ts/no-explicit-any */
38+
delete (global as any).document
39+
delete (global as any).window
40+
delete (global as any).NodeFilter
41+
delete (global as any).HTMLElement
42+
delete (global as any).Element
43+
delete (global as any).Node
44+
/* eslint-enable ts/no-explicit-any */
45+
}
46+
47+
/**
48+
* Create a test element with content
49+
*/
50+
export function createTestElement(content: string, tagName = 'div'): HTMLElement {
51+
const element = document.createElement(tagName)
52+
element.textContent = content
53+
document.body.appendChild(element)
54+
return element
55+
}
56+
57+
/**
58+
* Get playground directory path for E2E tests
59+
*/
60+
export function getPlaygroundPath() {
61+
return fileURLToPath(new URL('../../playground', import.meta.url))
62+
}
63+
64+
/**
65+
* Get test fixtures directory path
66+
*/
67+
export function getFixturesPath() {
68+
return fileURLToPath(new URL('../fixtures', import.meta.url))
69+
}
70+
71+
/**
72+
* Create a test config with overrides
73+
*/
74+
export function createTestConfig(overrides: Partial<SuperscriptConfig> = {}): SuperscriptConfig {
75+
return {
76+
...DEFAULT_CONFIG,
77+
...overrides,
78+
symbols: {
79+
...DEFAULT_CONFIG.symbols,
80+
...(overrides.symbols || {}),
81+
},
82+
performance: {
83+
...DEFAULT_CONFIG.performance,
84+
...(overrides.performance || {}),
85+
},
86+
cssVariables: {
87+
...DEFAULT_CONFIG.cssVariables,
88+
...(overrides.cssVariables || {}),
89+
},
90+
}
91+
}
92+
93+
/**
94+
* Wait for DOM mutations (useful for testing MutationObserver)
95+
*/
96+
export function waitForMutations(timeout = 100): Promise<void> {
97+
return new Promise((resolve) => setTimeout(resolve, timeout))
98+
}
99+
100+
/**
101+
* Count transformed elements in the DOM
102+
*/
103+
export function countTransformedElements() {
104+
return {
105+
total: document.querySelectorAll('[class^="ss-"]').length,
106+
trademarks: document.querySelectorAll('.ss-tm').length,
107+
registered: document.querySelectorAll('.ss-reg').length,
108+
superscripts: document.querySelectorAll('.ss-sup').length,
109+
subscripts: document.querySelectorAll('.ss-sub').length,
110+
math: document.querySelectorAll('.ss-math').length,
111+
ordinals: document.querySelectorAll('.ss-ord').length,
112+
chemicals: document.querySelectorAll('.ss-chem').length,
113+
}
114+
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/**
2+
* Integration tests for element positioning
3+
* Verifies that SPAN elements can be positioned while SUP/SUB use native positioning
4+
*/
5+
6+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
7+
import { processContent } from '../../src/runtime/smartscript/engine'
8+
import { createCombinedPattern, createPatterns } from '../../src/runtime/smartscript/patterns'
9+
import { cleanupDOM, createTestConfig, createTestElement, setupDOM } from '../helpers/setup'
10+
11+
describe('element Positioning Integration', () => {
12+
beforeEach(() => {
13+
setupDOM('<!DOCTYPE html><html><head><style></style></head><body></body></html>')
14+
})
15+
16+
afterEach(() => {
17+
cleanupDOM()
18+
})
19+
20+
describe('trademark and Registered Positioning', () => {
21+
it('should create SPAN elements for TM that can be positioned', () => {
22+
const container = createTestElement('Product(TM) and Brand(R)', 'div')
23+
container.className = 'content'
24+
25+
const config = createTestConfig({
26+
selectors: {
27+
include: ['.content'],
28+
exclude: [],
29+
},
30+
})
31+
const patterns = createPatterns(config)
32+
const combinedPattern = createCombinedPattern(patterns, config)
33+
processContent(config, patterns, combinedPattern)
34+
35+
// Find the transformed elements
36+
const tmElements = document.querySelectorAll('span.ss-tm')
37+
const regElements = document.querySelectorAll('span.ss-reg')
38+
39+
expect(tmElements.length).toBe(1)
40+
expect(regElements.length).toBe(1)
41+
42+
// Verify they are SPAN elements
43+
expect(tmElements[0].tagName).toBe('SPAN')
44+
expect(regElements[0].tagName).toBe('SPAN')
45+
})
46+
47+
it('should apply CSS variables to positioned elements', () => {
48+
const style = document.createElement('style')
49+
style.textContent = `
50+
:root {
51+
--ss-tm-top: -0.2em;
52+
--ss-reg-top: -0.25em;
53+
}
54+
span.ss-tm {
55+
position: relative;
56+
top: var(--ss-tm-top, -0.3em);
57+
}
58+
span.ss-reg {
59+
position: relative;
60+
top: var(--ss-reg-top, -0.3em);
61+
}
62+
`
63+
document.head.appendChild(style)
64+
65+
const container = createTestElement('MITRE ACT(TM)', 'div')
66+
container.className = 'content'
67+
68+
const config = createTestConfig({
69+
selectors: {
70+
include: ['.content'],
71+
exclude: [],
72+
},
73+
})
74+
const patterns = createPatterns(config)
75+
const combinedPattern = createCombinedPattern(patterns, config)
76+
processContent(config, patterns, combinedPattern)
77+
78+
const tm = document.querySelector('span.ss-tm') as HTMLElement
79+
expect(tm).toBeTruthy()
80+
expect(tm.tagName).toBe('SPAN')
81+
})
82+
})
83+
84+
describe('math and Ordinal Native Positioning', () => {
85+
it('should create SUP elements for math that use native positioning', () => {
86+
const container = createTestElement('E=mc^2 and x^3', 'div')
87+
container.className = 'content'
88+
89+
const config = createTestConfig({
90+
selectors: {
91+
include: ['.content'],
92+
exclude: [],
93+
},
94+
})
95+
const patterns = createPatterns(config)
96+
const combinedPattern = createCombinedPattern(patterns, config)
97+
processContent(config, patterns, combinedPattern)
98+
99+
const mathElements = document.querySelectorAll('sup.ss-math')
100+
expect(mathElements.length).toBe(2)
101+
102+
// Verify they are SUP elements
103+
mathElements.forEach((el) => {
104+
expect(el.tagName).toBe('SUP')
105+
})
106+
})
107+
108+
it('should create SUP elements for ordinals', () => {
109+
const container = createTestElement('1st place and 2nd place', 'div')
110+
container.className = 'content'
111+
112+
const config = createTestConfig({
113+
selectors: {
114+
include: ['.content'],
115+
exclude: [],
116+
},
117+
})
118+
const patterns = createPatterns(config)
119+
const combinedPattern = createCombinedPattern(patterns, config)
120+
processContent(config, patterns, combinedPattern)
121+
122+
const ordinalElements = document.querySelectorAll('sup.ss-ordinal')
123+
expect(ordinalElements.length).toBe(2)
124+
125+
// Verify they are SUP elements
126+
ordinalElements.forEach((el) => {
127+
expect(el.tagName).toBe('SUP')
128+
})
129+
})
130+
})
131+
132+
describe('chemical Subscript Positioning', () => {
133+
it('should create SUB elements for chemical formulas', () => {
134+
const container = createTestElement('H2O and CO2', 'div')
135+
container.className = 'content'
136+
137+
const config = createTestConfig({
138+
selectors: {
139+
include: ['.content'],
140+
exclude: [],
141+
},
142+
})
143+
const patterns = createPatterns(config)
144+
const combinedPattern = createCombinedPattern(patterns, config)
145+
processContent(config, patterns, combinedPattern)
146+
147+
const subElements = document.querySelectorAll('sub.ss-sub')
148+
expect(subElements.length).toBe(2)
149+
150+
// Verify they are SUB elements
151+
subElements.forEach((el) => {
152+
expect(el.tagName).toBe('SUB')
153+
})
154+
})
155+
})
156+
157+
describe('mixed Content with Different Elements', () => {
158+
it('should use appropriate elements for mixed content', () => {
159+
const container = createTestElement('Product(TM) uses H2O in the 21st century with formula x^2', 'div')
160+
container.className = 'content'
161+
162+
const config = createTestConfig({
163+
selectors: {
164+
include: ['.content'],
165+
exclude: [],
166+
},
167+
})
168+
const patterns = createPatterns(config)
169+
const combinedPattern = createCombinedPattern(patterns, config)
170+
processContent(config, patterns, combinedPattern)
171+
172+
// Check TM is SPAN
173+
const tm = document.querySelector('span.ss-tm')
174+
expect(tm).toBeTruthy()
175+
expect(tm?.tagName).toBe('SPAN')
176+
177+
// Check chemical subscript is SUB
178+
const sub = document.querySelector('sub.ss-sub')
179+
expect(sub).toBeTruthy()
180+
expect(sub?.tagName).toBe('SUB')
181+
182+
// Check ordinal is SUP
183+
const ordinal = document.querySelector('sup.ss-ordinal')
184+
expect(ordinal).toBeTruthy()
185+
expect(ordinal?.tagName).toBe('SUP')
186+
187+
// Check math is SUP
188+
const math = document.querySelector('sup.ss-math')
189+
expect(math).toBeTruthy()
190+
expect(math?.tagName).toBe('SUP')
191+
})
192+
})
193+
})

0 commit comments

Comments
 (0)