Skip to content

Commit edabe2f

Browse files
committed
test(fe,component): add button built-in style specificity tests
1 parent 49642c0 commit edabe2f

1 file changed

Lines changed: 85 additions & 0 deletions

File tree

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import fs from 'node:fs'
2+
import { fileURLToPath } from 'node:url'
3+
import { describe, expect, it } from 'vitest'
4+
5+
const BUTTON_FILE = fileURLToPath(new URL('../src/component/button/Button.vue', import.meta.url))
6+
7+
function getBlock(source, tagName) {
8+
const match = source.match(new RegExp(`<${tagName}[^>]*>([\\s\\S]*?)</${tagName}>`))
9+
return match?.[1] ?? ''
10+
}
11+
12+
function collectStyleRules(styleBlock) {
13+
const rules = []
14+
let index = 0
15+
16+
while (index < styleBlock.length) {
17+
const open = styleBlock.indexOf('{', index)
18+
if (open === -1) break
19+
20+
const selector = styleBlock.slice(index, open).trim()
21+
let depth = 1
22+
let close = open + 1
23+
while (close < styleBlock.length && depth > 0) {
24+
const char = styleBlock[close]
25+
if (char === '{') depth += 1
26+
if (char === '}') depth -= 1
27+
close += 1
28+
}
29+
30+
if (selector && !selector.includes('}') && !selector.startsWith('@')) {
31+
rules.push({
32+
selector,
33+
body: styleBlock.slice(open + 1, close - 1),
34+
})
35+
}
36+
37+
index = close
38+
}
39+
40+
return rules
41+
}
42+
43+
function getRuleBody(rules, selector) {
44+
const rule = rules.find(item => item.selector === selector)
45+
expect(rule, `missing style rule: ${selector}`).toBeDefined()
46+
return rule.body
47+
}
48+
49+
describe('Button built-in style specificity', () => {
50+
const source = fs.readFileSync(BUTTON_FILE, 'utf-8')
51+
const template = getBlock(source, 'template')
52+
const style = getBlock(source, 'style')
53+
const rules = collectStyleRules(style)
54+
55+
it('does not rely on :where() for Android WebView compatibility', () => {
56+
expect(style).not.toContain(':where(')
57+
})
58+
59+
it('exposes class modifiers for styled button states', () => {
60+
expect(template).toContain('`dd-button--${type}`')
61+
expect(template).toContain("size === 'mini' && 'dd-button--mini'")
62+
expect(template).toContain("plainParsed && 'dd-button--plain'")
63+
expect(template).toContain("disabledParsed && 'dd-button--disabled'")
64+
expect(template).toContain("loadingParsed && 'dd-button--loading'")
65+
})
66+
67+
it('keeps class-based styles for important state combinations', () => {
68+
expect(getRuleBody(rules, '.dd-button--primary')).toContain('background-color: #1aad19')
69+
expect(getRuleBody(rules, '.dd-button--disabled.dd-button--primary')).toContain('background-color: #9ed99d')
70+
expect(getRuleBody(rules, '.dd-button--primary.dd-button--plain')).toContain('border: 1px solid #1aad19')
71+
expect(getRuleBody(rules, '.dd-button--primary.dd-button--plain.dd-button--disabled')).toContain('border-color: rgba(0, 0, 0, 0.2)')
72+
expect(getRuleBody(rules, '.dd-button--loading.dd-button--primary.dd-button--plain')).toContain('background-color: transparent')
73+
expect(getRuleBody(rules, '.button-hover.dd-button--primary.dd-button--plain')).toContain('border-color: rgba(26, 173, 25, 0.6)')
74+
})
75+
76+
it('does not keep legacy attribute selectors for class-backed states', () => {
77+
expect(style).not.toContain(".dd-button[type='primary']")
78+
expect(style).not.toContain(".dd-button[type='primary'][plain]")
79+
expect(style).not.toContain(".dd-button[disabled][type='primary']")
80+
expect(style).not.toContain(".dd-button[size='mini']")
81+
expect(style).not.toContain('.dd-button[loading]')
82+
expect(style).not.toContain(".button-hover[type='primary']")
83+
expect(style).not.toContain('.button-hover[plain]')
84+
})
85+
})

0 commit comments

Comments
 (0)