|
1 | | -// @vitest-environment happy-dom |
2 | | -import { describe, it, expect, beforeEach } from 'vitest' |
| 1 | +// @vitest-environment node |
| 2 | +import { describe, it, expect } from 'vitest' |
3 | 3 | import { |
4 | 4 | clearFieldFeedback, |
5 | 5 | hideErrorBanner, |
6 | 6 | initLabelHandlers, |
7 | 7 | showErrorBanner, |
8 | 8 | showFieldFeedback, |
9 | 9 | } from '@components/Forms/Contact/client/feedback' |
10 | | -import type { ContactFormFields, FieldElements } from '@components/Forms/Contact/client/@types' |
11 | | - |
12 | | -const createField = <T extends HTMLInputElement | HTMLTextAreaElement>( |
13 | | - input: T, |
14 | | - id: string, |
15 | | -): FieldElements<T> => { |
16 | | - const label = document.createElement('label') |
17 | | - label.htmlFor = id |
18 | | - const feedback = document.createElement('p') |
19 | | - feedback.dataset['fieldError'] = id |
20 | | - feedback.classList.add('hidden') |
21 | | - return { input, label, feedback } |
22 | | -} |
23 | | - |
24 | | -const createFields = (): ContactFormFields => { |
25 | | - const nameInput = Object.assign(document.createElement('input'), { |
26 | | - id: 'name', |
27 | | - value: '', |
28 | | - }) |
29 | | - const emailInput = Object.assign(document.createElement('input'), { |
30 | | - id: 'email', |
31 | | - type: 'email', |
32 | | - value: '', |
33 | | - }) |
34 | | - const messageInput = Object.assign(document.createElement('textarea'), { |
35 | | - id: 'message', |
36 | | - value: '', |
37 | | - }) |
38 | | - |
39 | | - return { |
40 | | - name: createField(nameInput, 'name'), |
41 | | - email: createField(emailInput, 'email'), |
42 | | - message: createField(messageInput, 'message'), |
43 | | - } |
44 | | -} |
| 10 | +import { renderContactForm } from './testUtils' |
45 | 11 |
|
46 | 12 | describe('ContactForm feedback helpers', () => { |
47 | | - beforeEach(() => { |
48 | | - document.body.innerHTML = '' |
| 13 | + it('toggles error banner visibility', async () => { |
| 14 | + await renderContactForm(({ elements }) => { |
| 15 | + showErrorBanner(elements.formErrorBanner) |
| 16 | + expect(elements.formErrorBanner.classList.contains('hidden')).toBe(false) |
| 17 | + |
| 18 | + hideErrorBanner(elements.formErrorBanner) |
| 19 | + expect(elements.formErrorBanner.classList.contains('hidden')).toBe(true) |
| 20 | + }) |
49 | 21 | }) |
50 | 22 |
|
51 | | - it('toggles error banner visibility', () => { |
52 | | - const banner = document.createElement('div') |
53 | | - banner.classList.add('hidden') |
54 | | - |
55 | | - showErrorBanner(banner) |
56 | | - expect(banner.classList.contains('hidden')).toBe(false) |
57 | | - |
58 | | - hideErrorBanner(banner) |
59 | | - expect(banner.classList.contains('hidden')).toBe(true) |
60 | | - }) |
61 | | - |
62 | | - it('shows and clears error feedback for a field', () => { |
63 | | - const field = createField(Object.assign(document.createElement('input'), { id: 'email' }), 'email') |
64 | | - field.feedback.style.color = '' |
65 | | - |
66 | | - showFieldFeedback(field, 'Invalid email', 'error') |
67 | | - expect(field.feedback.textContent).toBe('Invalid email') |
68 | | - expect(field.feedback.classList.contains('hidden')).toBe(false) |
69 | | - expect(field.feedback.dataset['state']).toBe('error') |
70 | | - expect(field.feedback.style.color).toBe('var(--color-danger)') |
71 | | - expect(field.input.classList.contains('error')).toBe(true) |
72 | | - |
73 | | - clearFieldFeedback(field) |
74 | | - expect(field.feedback.textContent).toBe('') |
75 | | - expect(field.feedback.classList.contains('hidden')).toBe(true) |
76 | | - expect(field.feedback.dataset['state']).toBeUndefined() |
77 | | - expect(field.feedback.getAttribute('style')).toBeNull() |
78 | | - expect(field.input.classList.contains('error')).toBe(false) |
| 23 | + it('shows and clears error feedback for a field', async () => { |
| 24 | + await renderContactForm(({ elements }) => { |
| 25 | + const field = elements.fields.email |
| 26 | + |
| 27 | + showFieldFeedback(field, 'Invalid email', 'error') |
| 28 | + expect(field.feedback.textContent).toBe('Invalid email') |
| 29 | + expect(field.feedback.classList.contains('hidden')).toBe(false) |
| 30 | + expect(field.feedback.dataset['state']).toBe('error') |
| 31 | + expect(field.feedback.style.color).toBe('var(--color-danger)') |
| 32 | + expect(field.input.classList.contains('error')).toBe(true) |
| 33 | + |
| 34 | + clearFieldFeedback(field) |
| 35 | + expect(field.feedback.textContent).toBe('') |
| 36 | + expect(field.feedback.classList.contains('hidden')).toBe(true) |
| 37 | + expect(field.feedback.dataset['state']).toBeUndefined() |
| 38 | + expect(field.feedback.getAttribute('style')).toBeNull() |
| 39 | + expect(field.input.classList.contains('error')).toBe(false) |
| 40 | + }) |
79 | 41 | }) |
80 | 42 |
|
81 | | - it('supports warning-level feedback without marking the field as error', () => { |
82 | | - const field = createField(Object.assign(document.createElement('textarea'), { id: 'message' }), 'message') |
| 43 | + it('supports warning-level feedback without marking the field as error', async () => { |
| 44 | + await renderContactForm(({ elements }) => { |
| 45 | + const field = elements.fields.message |
83 | 46 |
|
84 | | - showFieldFeedback(field, 'Almost too long', 'warning') |
| 47 | + showFieldFeedback(field, 'Almost too long', 'warning') |
85 | 48 |
|
86 | | - expect(field.feedback.dataset['state']).toBe('warning') |
87 | | - expect(field.feedback.style.color).toBe('var(--color-warning)') |
88 | | - expect(field.input.classList.contains('error')).toBe(false) |
| 49 | + expect(field.feedback.dataset['state']).toBe('warning') |
| 50 | + expect(field.feedback.style.color).toBe('var(--color-warning)') |
| 51 | + expect(field.input.classList.contains('error')).toBe(false) |
| 52 | + }) |
89 | 53 | }) |
90 | 54 |
|
91 | | - it('initializes label handlers and syncs opacity based on field values', () => { |
92 | | - const fields = createFields() |
93 | | - const controller = initLabelHandlers(fields) |
| 55 | + it('initializes label handlers and syncs opacity based on field values', async () => { |
| 56 | + await renderContactForm(({ elements, window }) => { |
| 57 | + const controller = initLabelHandlers(elements.fields) |
94 | 58 |
|
95 | | - expect(fields.name.label.style.opacity).toBe('0.5') |
| 59 | + expect(elements.fields.name.label.style.opacity).toBe('0.5') |
96 | 60 |
|
97 | | - fields.name.input.value = 'Webstack' |
98 | | - fields.name.input.dispatchEvent(new Event('input')) |
99 | | - expect(fields.name.label.style.opacity).toBe('0') |
| 61 | + elements.fields.name.input.value = 'Webstack' |
| 62 | + elements.fields.name.input.dispatchEvent(new window.Event('input')) |
| 63 | + expect(elements.fields.name.label.style.opacity).toBe('0') |
100 | 64 |
|
101 | | - fields.name.input.value = '' |
102 | | - controller.sync() |
103 | | - expect(fields.name.label.style.opacity).toBe('0.5') |
| 65 | + elements.fields.name.input.value = '' |
| 66 | + controller.sync() |
| 67 | + expect(elements.fields.name.label.style.opacity).toBe('0.5') |
| 68 | + }) |
104 | 69 | }) |
105 | 70 | }) |
0 commit comments