@datametria/vue-components 1.2.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +548 -657
- package/dist/index.es.js +2353 -1364
- package/dist/index.umd.js +10 -10
- package/dist/vue-components.css +1 -1
- package/package.json +102 -98
- package/src/components/DatametriaAlert.vue +137 -137
- package/src/components/DatametriaAutocomplete.vue +184 -138
- package/src/components/DatametriaAvatar.vue +177 -33
- package/src/components/DatametriaBadge.vue +98 -98
- package/src/components/DatametriaBreadcrumb.vue +21 -21
- package/src/components/DatametriaButton.vue +177 -165
- package/src/components/DatametriaCard.vue +12 -12
- package/src/components/DatametriaCheckbox.vue +8 -8
- package/src/components/DatametriaChip.vue +145 -149
- package/src/components/DatametriaContainer.vue +4 -4
- package/src/components/DatametriaDatePicker.vue +686 -68
- package/src/components/DatametriaDivider.vue +13 -13
- package/src/components/DatametriaFileUpload.vue +272 -140
- package/src/components/DatametriaGrid.vue +3 -3
- package/src/components/DatametriaInput.vue +15 -15
- package/src/components/DatametriaMenu.vue +604 -619
- package/src/components/DatametriaModal.vue +16 -16
- package/src/components/DatametriaNavbar.vue +230 -252
- package/src/components/DatametriaPasswordInput.vue +430 -0
- package/src/components/DatametriaProgress.vue +18 -18
- package/src/components/DatametriaRadio.vue +20 -20
- package/src/components/DatametriaSelect.vue +15 -15
- package/src/components/DatametriaSkeleton.vue +243 -239
- package/src/components/DatametriaSlider.vue +395 -407
- package/src/components/DatametriaSortableTable.vue +585 -0
- package/src/components/DatametriaSpinner.vue +7 -7
- package/src/components/DatametriaSwitch.vue +16 -16
- package/src/components/DatametriaTable.vue +14 -14
- package/src/components/DatametriaTextarea.vue +28 -28
- package/src/components/DatametriaTimePicker.vue +285 -285
- package/src/components/DatametriaToast.vue +176 -176
- package/src/components/DatametriaTooltip.vue +408 -408
- package/src/components/__tests__/DatametriaAlert.test.js +35 -35
- package/src/components/__tests__/DatametriaAlert.test.ts +190 -0
- package/src/components/__tests__/DatametriaAutocomplete.test.ts +180 -0
- package/src/components/__tests__/DatametriaAvatar.test.ts +152 -0
- package/src/components/__tests__/DatametriaBadge.test.js +29 -29
- package/src/components/__tests__/DatametriaBadge.test.ts +167 -0
- package/src/components/__tests__/DatametriaBreadcrumb.test.ts +75 -0
- package/src/components/__tests__/DatametriaButton.test.js +30 -30
- package/src/components/__tests__/DatametriaButton.test.ts +283 -0
- package/src/components/__tests__/DatametriaCard.test.ts +201 -0
- package/src/components/__tests__/DatametriaCheckbox.test.ts +47 -0
- package/src/components/__tests__/DatametriaChip.test.js +38 -38
- package/src/components/__tests__/DatametriaContainer.test.ts +52 -0
- package/src/components/__tests__/DatametriaDatePicker.test.ts +234 -0
- package/src/components/__tests__/DatametriaDivider.test.ts +54 -0
- package/src/components/__tests__/DatametriaFileUpload.test.ts +291 -0
- package/src/components/__tests__/DatametriaGrid.test.ts +31 -0
- package/src/components/__tests__/DatametriaInput.test.ts +72 -0
- package/src/components/__tests__/DatametriaMenu.test.ts +366 -0
- package/src/components/__tests__/DatametriaModal.test.ts +86 -0
- package/src/components/__tests__/DatametriaNavbar.test.js +48 -48
- package/src/components/__tests__/DatametriaNavbar.test.ts +203 -0
- package/src/components/__tests__/DatametriaPasswordInput.test.js +305 -0
- package/src/components/__tests__/DatametriaProgress.test.ts +90 -0
- package/src/components/__tests__/DatametriaRadio.test.ts +77 -0
- package/src/components/__tests__/DatametriaSelect.test.ts +77 -0
- package/src/components/__tests__/DatametriaSlider.test.ts +261 -0
- package/src/components/__tests__/DatametriaSortableTable.test.js +168 -0
- package/src/components/__tests__/DatametriaSpinner.test.ts +156 -0
- package/src/components/__tests__/DatametriaSwitch.test.ts +64 -0
- package/src/components/__tests__/DatametriaTable.test.ts +97 -0
- package/src/components/__tests__/DatametriaTextarea.test.ts +66 -0
- package/src/components/__tests__/DatametriaToast.test.js +48 -48
- package/src/components/__tests__/DatametriaToast.test.ts +99 -0
- package/src/composables/useAccessibilityScale.ts +94 -94
- package/src/composables/useBreakpoints.ts +82 -82
- package/src/composables/useHapticFeedback.ts +439 -439
- package/src/composables/useRipple.ts +218 -218
- package/src/index.ts +68 -61
- package/src/stories/Variants.stories.js +95 -95
- package/src/styles/design-tokens.css +623 -623
- package/src/theme/ThemeProvider.vue +96 -0
- package/src/theme/__tests__/ThemeProvider.test.ts +208 -0
- package/src/theme/__tests__/constants.test.ts +31 -0
- package/src/theme/__tests__/presets.test.ts +166 -0
- package/src/theme/__tests__/tokens.test.ts +155 -0
- package/src/theme/__tests__/types.test.ts +153 -0
- package/src/theme/__tests__/useTheme.test.ts +146 -0
- package/src/theme/constants.ts +14 -0
- package/src/theme/index.ts +12 -0
- package/src/theme/presets/datametria.ts +94 -0
- package/src/theme/presets/default.ts +94 -0
- package/src/theme/presets/index.ts +8 -0
- package/src/theme/tokens/colors.ts +28 -0
- package/src/theme/tokens/index.ts +47 -0
- package/src/theme/tokens/spacing.ts +21 -0
- package/src/theme/tokens/typography.ts +35 -0
- package/src/theme/types.ts +111 -0
- package/src/theme/useTheme.ts +28 -0
- package/src/types/index.ts +19 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import DatametriaNavbar from '../DatametriaNavbar.vue'
|
|
4
|
+
|
|
5
|
+
describe('DatametriaNavbar', () => {
|
|
6
|
+
describe('Renderização', () => {
|
|
7
|
+
it('renderiza corretamente', () => {
|
|
8
|
+
const wrapper = mount(DatametriaNavbar)
|
|
9
|
+
expect(wrapper.find('.dm-navbar').exists()).toBe(true)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('renderiza brand text', () => {
|
|
13
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
14
|
+
props: { brand: 'DATAMETRIA' }
|
|
15
|
+
})
|
|
16
|
+
expect(wrapper.text()).toContain('DATAMETRIA')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('renderiza slot brand customizado', () => {
|
|
20
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
21
|
+
slots: { brand: '<div class="custom-brand">Custom</div>' }
|
|
22
|
+
})
|
|
23
|
+
expect(wrapper.find('.custom-brand').exists()).toBe(true)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('renderiza slot menu', () => {
|
|
27
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
28
|
+
slots: { menu: '<div class="menu-items">Items</div>' }
|
|
29
|
+
})
|
|
30
|
+
expect(wrapper.find('.menu-items').exists()).toBe(true)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('renderiza slot actions', () => {
|
|
34
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
35
|
+
slots: { actions: '<button>Login</button>' }
|
|
36
|
+
})
|
|
37
|
+
expect(wrapper.find('.dm-navbar__actions').exists()).toBe(true)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe('Variants', () => {
|
|
42
|
+
it('aplica variant light por padrão', () => {
|
|
43
|
+
const wrapper = mount(DatametriaNavbar)
|
|
44
|
+
expect(wrapper.find('.dm-navbar').classes()).toContain('dm-navbar--light')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('aplica variant dark', () => {
|
|
48
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
49
|
+
props: { variant: 'dark' }
|
|
50
|
+
})
|
|
51
|
+
expect(wrapper.find('.dm-navbar').classes()).toContain('dm-navbar--dark')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('aplica variant primary', () => {
|
|
55
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
56
|
+
props: { variant: 'primary' }
|
|
57
|
+
})
|
|
58
|
+
expect(wrapper.find('.dm-navbar').classes()).toContain('dm-navbar--primary')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('aplica variant transparent', () => {
|
|
62
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
63
|
+
props: { variant: 'transparent' }
|
|
64
|
+
})
|
|
65
|
+
expect(wrapper.find('.dm-navbar').classes()).toContain('dm-navbar--transparent')
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('Props', () => {
|
|
70
|
+
it('aceita sticky', () => {
|
|
71
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
72
|
+
props: { sticky: true }
|
|
73
|
+
})
|
|
74
|
+
expect(wrapper.find('.dm-navbar').classes()).toContain('dm-navbar--sticky')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('aceita transparent', () => {
|
|
78
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
79
|
+
props: { transparent: true }
|
|
80
|
+
})
|
|
81
|
+
expect(wrapper.find('.dm-navbar').classes()).toContain('dm-navbar--transparent')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('aceita bordered', () => {
|
|
85
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
86
|
+
props: { bordered: true }
|
|
87
|
+
})
|
|
88
|
+
expect(wrapper.find('.dm-navbar').classes()).toContain('dm-navbar--bordered')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('aceita ariaLabel customizado', () => {
|
|
92
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
93
|
+
props: { ariaLabel: 'Custom navigation' }
|
|
94
|
+
})
|
|
95
|
+
expect(wrapper.find('nav').attributes('aria-label')).toBe('Custom navigation')
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe('Mobile Menu', () => {
|
|
100
|
+
it('mostra toggle quando há slot menu', () => {
|
|
101
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
102
|
+
slots: { menu: '<div>Menu</div>' }
|
|
103
|
+
})
|
|
104
|
+
expect(wrapper.find('.dm-navbar__toggle').exists()).toBe(true)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('não mostra toggle quando não há slot menu', () => {
|
|
108
|
+
const wrapper = mount(DatametriaNavbar)
|
|
109
|
+
expect(wrapper.find('.dm-navbar__toggle').exists()).toBe(false)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('abre menu ao clicar no toggle', async () => {
|
|
113
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
114
|
+
slots: { menu: '<div>Menu</div>' }
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const toggle = wrapper.find('.dm-navbar__toggle')
|
|
118
|
+
await toggle.trigger('click')
|
|
119
|
+
|
|
120
|
+
expect(wrapper.find('.dm-navbar__menu').classes()).toContain('dm-navbar__menu--open')
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('fecha menu ao clicar novamente no toggle', async () => {
|
|
124
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
125
|
+
slots: { menu: '<div>Menu</div>' }
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const toggle = wrapper.find('.dm-navbar__toggle')
|
|
129
|
+
await toggle.trigger('click')
|
|
130
|
+
await toggle.trigger('click')
|
|
131
|
+
|
|
132
|
+
expect(wrapper.find('.dm-navbar__menu').classes()).not.toContain('dm-navbar__menu--open')
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
describe('Acessibilidade', () => {
|
|
137
|
+
it('tem role navigation', () => {
|
|
138
|
+
const wrapper = mount(DatametriaNavbar)
|
|
139
|
+
expect(wrapper.find('nav').attributes('role')).toBe('navigation')
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('tem aria-label padrão', () => {
|
|
143
|
+
const wrapper = mount(DatametriaNavbar)
|
|
144
|
+
expect(wrapper.find('nav').attributes('aria-label')).toBe('Main navigation')
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('toggle tem aria-expanded', () => {
|
|
148
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
149
|
+
slots: { menu: '<div>Menu</div>' }
|
|
150
|
+
})
|
|
151
|
+
expect(wrapper.find('.dm-navbar__toggle').attributes('aria-expanded')).toBe('false')
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('toggle atualiza aria-expanded quando aberto', async () => {
|
|
155
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
156
|
+
slots: { menu: '<div>Menu</div>' }
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
await wrapper.find('.dm-navbar__toggle').trigger('click')
|
|
160
|
+
expect(wrapper.find('.dm-navbar__toggle').attributes('aria-expanded')).toBe('true')
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('toggle tem aria-controls', () => {
|
|
164
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
165
|
+
slots: { menu: '<div>Menu</div>' }
|
|
166
|
+
})
|
|
167
|
+
expect(wrapper.find('.dm-navbar__toggle').attributes('aria-controls')).toBe('navbar-menu')
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('menu tem id correto', () => {
|
|
171
|
+
const wrapper = mount(DatametriaNavbar, {
|
|
172
|
+
slots: { menu: '<div>Menu</div>' }
|
|
173
|
+
})
|
|
174
|
+
expect(wrapper.find('.dm-navbar__menu').attributes('id')).toBe('navbar-menu')
|
|
175
|
+
})
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
describe('Validação', () => {
|
|
179
|
+
it('valida variant inválido em desenvolvimento', () => {
|
|
180
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
181
|
+
const originalEnv = process.env.NODE_ENV
|
|
182
|
+
process.env.NODE_ENV = 'development'
|
|
183
|
+
|
|
184
|
+
mount(DatametriaNavbar, {
|
|
185
|
+
props: { variant: 'invalid' as any }
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
189
|
+
expect.stringContaining('[DatametriaNavbar] Invalid variant "invalid"')
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
process.env.NODE_ENV = originalEnv
|
|
193
|
+
consoleSpy.mockRestore()
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
describe('CSS Variables', () => {
|
|
198
|
+
it('usa CSS Variables padronizadas', () => {
|
|
199
|
+
const wrapper = mount(DatametriaNavbar)
|
|
200
|
+
expect(wrapper.find('.dm-navbar').exists()).toBe(true)
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
})
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import DatametriaPasswordInput from '../DatametriaPasswordInput.vue'
|
|
4
|
+
|
|
5
|
+
describe('DatametriaPasswordInput', () => {
|
|
6
|
+
it('renders correctly', () => {
|
|
7
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
8
|
+
props: {
|
|
9
|
+
label: 'Senha'
|
|
10
|
+
}
|
|
11
|
+
})
|
|
12
|
+
expect(wrapper.find('.datametria-password-input').exists()).toBe(true)
|
|
13
|
+
expect(wrapper.find('label').text()).toContain('Senha')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('toggles password visibility', async () => {
|
|
17
|
+
const wrapper = mount(DatametriaPasswordInput)
|
|
18
|
+
const input = wrapper.find('input')
|
|
19
|
+
const toggleButton = wrapper.find('.datametria-password-input__toggle')
|
|
20
|
+
|
|
21
|
+
expect(input.attributes('type')).toBe('password')
|
|
22
|
+
|
|
23
|
+
await toggleButton.trigger('click')
|
|
24
|
+
expect(input.attributes('type')).toBe('text')
|
|
25
|
+
|
|
26
|
+
await toggleButton.trigger('click')
|
|
27
|
+
expect(input.attributes('type')).toBe('password')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('validates password strength', async () => {
|
|
31
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
32
|
+
props: {
|
|
33
|
+
modelValue: '',
|
|
34
|
+
showStrength: true
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// Weak password
|
|
39
|
+
await wrapper.setProps({ modelValue: 'abc' })
|
|
40
|
+
expect(wrapper.find('.datametria-password-input__strength-text--weak').exists()).toBe(true)
|
|
41
|
+
|
|
42
|
+
// Strong password
|
|
43
|
+
await wrapper.setProps({ modelValue: 'Abc123!@#' })
|
|
44
|
+
expect(wrapper.find('.datametria-password-input__strength-text--strong').exists()).toBe(true)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('shows requirements when focused', async () => {
|
|
48
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
49
|
+
props: {
|
|
50
|
+
showRequirements: true
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const input = wrapper.find('input')
|
|
55
|
+
|
|
56
|
+
expect(wrapper.find('.datametria-password-input__requirements').exists()).toBe(false)
|
|
57
|
+
|
|
58
|
+
await input.trigger('focus')
|
|
59
|
+
expect(wrapper.find('.datametria-password-input__requirements').exists()).toBe(true)
|
|
60
|
+
|
|
61
|
+
await input.trigger('blur')
|
|
62
|
+
expect(wrapper.find('.datametria-password-input__requirements').exists()).toBe(false)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('validates all requirements', async () => {
|
|
66
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
67
|
+
props: {
|
|
68
|
+
modelValue: 'Abc123!@#',
|
|
69
|
+
showRequirements: true
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
await wrapper.find('input').trigger('focus')
|
|
74
|
+
|
|
75
|
+
const requirements = wrapper.findAll('.datametria-password-input__requirements-list li')
|
|
76
|
+
expect(requirements).toHaveLength(5)
|
|
77
|
+
|
|
78
|
+
// All requirements should be valid
|
|
79
|
+
requirements.forEach(req => {
|
|
80
|
+
expect(req.classes()).toContain('valid')
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('emits update:modelValue on input', async () => {
|
|
85
|
+
const wrapper = mount(DatametriaPasswordInput)
|
|
86
|
+
const input = wrapper.find('input')
|
|
87
|
+
|
|
88
|
+
await input.setValue('test123')
|
|
89
|
+
expect(wrapper.emitted('update:modelValue')).toBeTruthy()
|
|
90
|
+
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['test123'])
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('emits strength-change event', async () => {
|
|
94
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
95
|
+
props: {
|
|
96
|
+
modelValue: ''
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
await wrapper.setProps({ modelValue: 'Abc123!@#' })
|
|
101
|
+
await wrapper.vm.$nextTick()
|
|
102
|
+
|
|
103
|
+
expect(wrapper.emitted('strength-change')).toBeTruthy()
|
|
104
|
+
expect(wrapper.emitted('strength-change')?.[0]?.[0]).toBe(100)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('shows error message', () => {
|
|
108
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
109
|
+
props: {
|
|
110
|
+
errorMessage: 'Senha inválida'
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
expect(wrapper.find('.datametria-password-input__error').text()).toBe('Senha inválida')
|
|
115
|
+
expect(wrapper.find('input').classes()).toContain('datametria-password-input__field--error')
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('shows help text', () => {
|
|
119
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
120
|
+
props: {
|
|
121
|
+
helpText: 'Use uma senha forte'
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
expect(wrapper.find('.datametria-password-input__help').text()).toBe('Use uma senha forte')
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('marks required field', () => {
|
|
129
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
130
|
+
props: {
|
|
131
|
+
label: 'Senha',
|
|
132
|
+
required: true
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
expect(wrapper.find('.datametria-password-input__required').exists()).toBe(true)
|
|
137
|
+
expect(wrapper.find('input').attributes('required')).toBeDefined()
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('disables input when disabled prop is true', () => {
|
|
141
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
142
|
+
props: {
|
|
143
|
+
disabled: true
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const input = wrapper.find('input')
|
|
148
|
+
expect(input.attributes('disabled')).toBeDefined()
|
|
149
|
+
expect(input.classes()).toContain('datametria-password-input__field--disabled')
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('sets correct autocomplete attribute', () => {
|
|
153
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
154
|
+
props: {
|
|
155
|
+
autocomplete: 'new-password'
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
expect(wrapper.find('input').attributes('autocomplete')).toBe('new-password')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('has proper ARIA attributes', async () => {
|
|
163
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
164
|
+
props: {
|
|
165
|
+
errorMessage: 'Senha inválida',
|
|
166
|
+
showRequirements: true
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
const input = wrapper.find('input')
|
|
171
|
+
|
|
172
|
+
expect(input.attributes('aria-invalid')).toBe('true')
|
|
173
|
+
|
|
174
|
+
await input.trigger('focus')
|
|
175
|
+
expect(input.attributes('aria-describedby')).toBeTruthy()
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('toggle button has proper accessibility', () => {
|
|
179
|
+
const wrapper = mount(DatametriaPasswordInput)
|
|
180
|
+
const toggleButton = wrapper.find('.datametria-password-input__toggle')
|
|
181
|
+
|
|
182
|
+
expect(toggleButton.attributes('aria-label')).toBe('Mostrar senha')
|
|
183
|
+
expect(toggleButton.attributes('type')).toBe('button')
|
|
184
|
+
expect(toggleButton.attributes('tabindex')).toBe('-1')
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
describe('CSS Variables Integration', () => {
|
|
188
|
+
it('uses semantic color tokens for strength indicators', async () => {
|
|
189
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
190
|
+
props: {
|
|
191
|
+
modelValue: 'Abc123!@#',
|
|
192
|
+
showStrength: true
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
expect(wrapper.find('.datametria-password-input__strength-fill--strong').exists()).toBe(true)
|
|
197
|
+
expect(wrapper.find('.datametria-password-input__strength-text--strong').exists()).toBe(true)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('uses error token for error states', () => {
|
|
201
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
202
|
+
props: {
|
|
203
|
+
errorMessage: 'Error'
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
expect(wrapper.find('.datametria-password-input__field--error').exists()).toBe(true)
|
|
208
|
+
expect(wrapper.find('.datametria-password-input__error').exists()).toBe(true)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('uses warning token for caps lock warning', async () => {
|
|
212
|
+
const wrapper = mount(DatametriaPasswordInput)
|
|
213
|
+
const input = wrapper.find('input')
|
|
214
|
+
|
|
215
|
+
const event = new KeyboardEvent('keyup', { key: 'A' })
|
|
216
|
+
Object.defineProperty(event, 'getModifierState', {
|
|
217
|
+
value: vi.fn(() => true)
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
await input.element.dispatchEvent(event)
|
|
221
|
+
await wrapper.vm.$nextTick()
|
|
222
|
+
|
|
223
|
+
expect(wrapper.find('.datametria-password-input__warning').exists()).toBe(true)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
it('uses success token for valid requirements', async () => {
|
|
227
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
228
|
+
props: {
|
|
229
|
+
modelValue: 'Abc123!@#',
|
|
230
|
+
showRequirements: true
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
await wrapper.find('input').trigger('focus')
|
|
235
|
+
const validItems = wrapper.findAll('.datametria-password-input__requirements-list li.valid')
|
|
236
|
+
expect(validItems.length).toBe(5)
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it('uses spacing tokens consistently', () => {
|
|
240
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
241
|
+
props: {
|
|
242
|
+
label: 'Password',
|
|
243
|
+
showRequirements: true
|
|
244
|
+
}
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
expect(wrapper.find('.datametria-password-input').exists()).toBe(true)
|
|
248
|
+
expect(wrapper.find('.datametria-password-input__field').exists()).toBe(true)
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
it('uses typography tokens for text elements', () => {
|
|
252
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
253
|
+
props: {
|
|
254
|
+
label: 'Password',
|
|
255
|
+
helpText: 'Help text'
|
|
256
|
+
}
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
expect(wrapper.find('.datametria-password-input__label').exists()).toBe(true)
|
|
260
|
+
expect(wrapper.find('.datametria-password-input__help').exists()).toBe(true)
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
it('uses transition tokens for animations', async () => {
|
|
264
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
265
|
+
props: {
|
|
266
|
+
modelValue: '',
|
|
267
|
+
showStrength: true
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
await wrapper.setProps({ modelValue: 'test' })
|
|
272
|
+
expect(wrapper.find('.datametria-password-input__strength-fill').exists()).toBe(true)
|
|
273
|
+
})
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
describe('Backward Compatibility', () => {
|
|
277
|
+
it('works without ThemeProvider', () => {
|
|
278
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
279
|
+
props: {
|
|
280
|
+
label: 'Password',
|
|
281
|
+
modelValue: 'Abc123!@#',
|
|
282
|
+
showStrength: true,
|
|
283
|
+
showRequirements: true
|
|
284
|
+
}
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
expect(wrapper.find('.datametria-password-input').exists()).toBe(true)
|
|
288
|
+
expect(wrapper.find('.datametria-password-input__strength').exists()).toBe(true)
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it('maintains original visual appearance with fallback values', async () => {
|
|
292
|
+
const wrapper = mount(DatametriaPasswordInput, {
|
|
293
|
+
props: {
|
|
294
|
+
modelValue: 'weak',
|
|
295
|
+
showStrength: true
|
|
296
|
+
}
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
expect(wrapper.find('.datametria-password-input__strength-fill--weak').exists()).toBe(true)
|
|
300
|
+
|
|
301
|
+
await wrapper.setProps({ modelValue: 'Abc123!@#' })
|
|
302
|
+
expect(wrapper.find('.datametria-password-input__strength-fill--strong').exists()).toBe(true)
|
|
303
|
+
})
|
|
304
|
+
})
|
|
305
|
+
})
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import DatametriaProgress from '../DatametriaProgress.vue'
|
|
4
|
+
|
|
5
|
+
describe('DatametriaProgress', () => {
|
|
6
|
+
it('renders with value', () => {
|
|
7
|
+
const wrapper = mount(DatametriaProgress, {
|
|
8
|
+
props: { value: 50 }
|
|
9
|
+
})
|
|
10
|
+
expect(wrapper.find('.dm-progress').exists()).toBe(true)
|
|
11
|
+
expect(wrapper.find('.dm-progress__bar').attributes('style')).toContain('width: 50%')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('renders with label', () => {
|
|
15
|
+
const wrapper = mount(DatametriaProgress, {
|
|
16
|
+
props: { value: 75, label: 'Loading' }
|
|
17
|
+
})
|
|
18
|
+
expect(wrapper.find('.dm-progress__label').text()).toBe('Loading')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('shows value by default', () => {
|
|
22
|
+
const wrapper = mount(DatametriaProgress, {
|
|
23
|
+
props: { value: 60, label: 'Test' }
|
|
24
|
+
})
|
|
25
|
+
expect(wrapper.find('.dm-progress__value').text()).toBe('60%')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('hides value when showValue is false', () => {
|
|
29
|
+
const wrapper = mount(DatametriaProgress, {
|
|
30
|
+
props: { value: 60, label: 'Test', showValue: false }
|
|
31
|
+
})
|
|
32
|
+
expect(wrapper.find('.dm-progress__value').exists()).toBe(false)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('clamps value to 0-100 range', () => {
|
|
36
|
+
const wrapper1 = mount(DatametriaProgress, { props: { value: -10 } })
|
|
37
|
+
expect(wrapper1.find('.dm-progress__bar').attributes('style')).toContain('width: 0%')
|
|
38
|
+
|
|
39
|
+
const wrapper2 = mount(DatametriaProgress, { props: { value: 150 } })
|
|
40
|
+
expect(wrapper2.find('.dm-progress__bar').attributes('style')).toContain('width: 100%')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('renders primary variant', () => {
|
|
44
|
+
const wrapper = mount(DatametriaProgress, {
|
|
45
|
+
props: { value: 50, variant: 'primary' }
|
|
46
|
+
})
|
|
47
|
+
expect(wrapper.find('.dm-progress__bar--primary').exists()).toBe(true)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('renders success variant', () => {
|
|
51
|
+
const wrapper = mount(DatametriaProgress, {
|
|
52
|
+
props: { value: 50, variant: 'success' }
|
|
53
|
+
})
|
|
54
|
+
expect(wrapper.find('.dm-progress__bar--success').exists()).toBe(true)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('renders warning variant', () => {
|
|
58
|
+
const wrapper = mount(DatametriaProgress, {
|
|
59
|
+
props: { value: 50, variant: 'warning' }
|
|
60
|
+
})
|
|
61
|
+
expect(wrapper.find('.dm-progress__bar--warning').exists()).toBe(true)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('renders error variant', () => {
|
|
65
|
+
const wrapper = mount(DatametriaProgress, {
|
|
66
|
+
props: { value: 50, variant: 'error' }
|
|
67
|
+
})
|
|
68
|
+
expect(wrapper.find('.dm-progress__bar--error').exists()).toBe(true)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('has correct ARIA attributes', () => {
|
|
72
|
+
const wrapper = mount(DatametriaProgress, {
|
|
73
|
+
props: { value: 75, label: 'Progress' }
|
|
74
|
+
})
|
|
75
|
+
const track = wrapper.find('.dm-progress__track')
|
|
76
|
+
expect(track.attributes('role')).toBe('progressbar')
|
|
77
|
+
expect(track.attributes('aria-valuenow')).toBe('75')
|
|
78
|
+
expect(track.attributes('aria-valuemin')).toBe('0')
|
|
79
|
+
expect(track.attributes('aria-valuemax')).toBe('100')
|
|
80
|
+
expect(track.attributes('aria-label')).toBe('Progress')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('works without ThemeProvider', () => {
|
|
84
|
+
const wrapper = mount(DatametriaProgress, {
|
|
85
|
+
props: { value: 80, label: 'Test', variant: 'success' }
|
|
86
|
+
})
|
|
87
|
+
expect(wrapper.find('.dm-progress__bar--success').exists()).toBe(true)
|
|
88
|
+
expect(wrapper.find('.dm-progress__bar').attributes('style')).toContain('width: 80%')
|
|
89
|
+
})
|
|
90
|
+
})
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import DatametriaRadio from '../DatametriaRadio.vue'
|
|
4
|
+
|
|
5
|
+
describe('DatametriaRadio', () => {
|
|
6
|
+
it('renders correctly', () => {
|
|
7
|
+
const wrapper = mount(DatametriaRadio, {
|
|
8
|
+
props: { value: 'option1' }
|
|
9
|
+
})
|
|
10
|
+
expect(wrapper.find('.dm-radio').exists()).toBe(true)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('renders label when provided', () => {
|
|
14
|
+
const wrapper = mount(DatametriaRadio, {
|
|
15
|
+
props: { value: 'option1', label: 'Option 1' }
|
|
16
|
+
})
|
|
17
|
+
expect(wrapper.find('.dm-radio__text').text()).toBe('Option 1')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('checks radio when modelValue matches value', () => {
|
|
21
|
+
const wrapper = mount(DatametriaRadio, {
|
|
22
|
+
props: { value: 'option1', modelValue: 'option1' }
|
|
23
|
+
})
|
|
24
|
+
expect((wrapper.find('input').element as HTMLInputElement).checked).toBe(true)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('emits update:modelValue on change', async () => {
|
|
28
|
+
const wrapper = mount(DatametriaRadio, {
|
|
29
|
+
props: { value: 'option1' }
|
|
30
|
+
})
|
|
31
|
+
await wrapper.find('input').trigger('change')
|
|
32
|
+
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['option1'])
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('applies disabled state', () => {
|
|
36
|
+
const wrapper = mount(DatametriaRadio, {
|
|
37
|
+
props: { value: 'option1', disabled: true }
|
|
38
|
+
})
|
|
39
|
+
expect(wrapper.find('input').attributes('disabled')).toBeDefined()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('does not emit when disabled', async () => {
|
|
43
|
+
const wrapper = mount(DatametriaRadio, {
|
|
44
|
+
props: { value: 'option1', disabled: true }
|
|
45
|
+
})
|
|
46
|
+
await wrapper.find('input').trigger('change')
|
|
47
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('shows error message when provided', () => {
|
|
51
|
+
const wrapper = mount(DatametriaRadio, {
|
|
52
|
+
props: { value: 'option1', name: 'test', error: 'Error text' }
|
|
53
|
+
})
|
|
54
|
+
expect(wrapper.find('.dm-radio__error').text()).toBe('Error text')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('applies name attribute', () => {
|
|
58
|
+
const wrapper = mount(DatametriaRadio, {
|
|
59
|
+
props: { value: 'option1', name: 'radio-group' }
|
|
60
|
+
})
|
|
61
|
+
expect(wrapper.find('input').attributes('name')).toBe('radio-group')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('applies aria-label', () => {
|
|
65
|
+
const wrapper = mount(DatametriaRadio, {
|
|
66
|
+
props: { value: 'option1', ariaLabel: 'Select option' }
|
|
67
|
+
})
|
|
68
|
+
expect(wrapper.find('input').attributes('aria-label')).toBe('Select option')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('uses CSS variables with fallbacks', () => {
|
|
72
|
+
const wrapper = mount(DatametriaRadio, {
|
|
73
|
+
props: { value: 'option1' }
|
|
74
|
+
})
|
|
75
|
+
expect(wrapper.html()).toContain('dm-radio')
|
|
76
|
+
})
|
|
77
|
+
})
|