Skip to content

Commit 7967b3a

Browse files
Merge pull request #1 from LeonardoToledo0/develop
test: adiciona novos testes unitários e de integração
2 parents e80d152 + caae11a commit 7967b3a

File tree

20 files changed

+2771
-238
lines changed

20 files changed

+2771
-238
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,8 @@ jobs:
2323
- name: 📦 Instalar dependências
2424
run: yarn install --frozen-lockfile
2525

26+
- name: 🧪 Executar testes
27+
run: yarn test
28+
2629
- name: 🛠️ Build do projeto
2730
run: yarn build

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ COPY package*.json ./
77
RUN yarn
88

99
COPY . .
10+
RUN yarn test
1011
RUN yarn build
1112

1213
# Etapa de produção

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,23 @@ cd front-end_charllenge-comerc
7575

7676
---
7777

78+
### 🧰 Rodando Testes
79+
80+
| Etapa | Comando |
81+
| -------------------------------------------- | ----------------- |
82+
| Executa todos os testes uma vez | `yarn test` |
83+
| Executa os testes e fica observando mudanças | `yarn test:watch` |
84+
85+
---
86+
7887
### 🐳 Rodando com Docker
7988

80-
| Etapa | Comando |
81-
| --------------- | ----------------------------------------------------------- |
82-
| Build da imagem | `docker build -t comerc-app --no-cache .` |
83-
| Rodar container | `docker run -d -p 8080:80 --name meu-comerc-app comerc-app` |
89+
| Etapa | Comando |
90+
| --------------- | -------------------------------------------------------------------------------------------------------------- |
91+
| Build da imagem | `docker build -t comerc-app --no-cache .` |
92+
| Rodar container | `docker run -d -p 8080:80 --name meu-comerc-app comerc-app` |
93+
| Teste Container | `docker run --rm -v $(pwd):/app -w /app node:20 sh -c "yarn install --frozen-lockfile && yarn test"` |
94+
| Teste Watch | `docker run --rm -v $(pwd):/app -w /app -it node:20 sh -c "yarn install --frozen-lockfile && yarn test:watch"` |
8495

8596
---
8697

package.json

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
"scripts": {
77
"dev": "vite",
88
"build": "vue-tsc -b && vite build",
9-
"preview": "vite preview"
9+
"preview": "vite preview",
10+
"test": "vitest",
11+
"test:watch": "vitest --watch",
12+
"test:docker": "docker run --rm -v $(pwd):/app -w /app node:20 sh -c \"yarn install --frozen-lockfile && yarn test\"",
13+
"test:docker:watch": "docker run --rm -v $(pwd):/app -w /app -it node:20 sh -c \"yarn install --frozen-lockfile && yarn test:watch\""
1014
},
1115
"dependencies": {
1216
"@heroicons/vue": "^2.2.0",
@@ -26,15 +30,19 @@
2630
},
2731
"devDependencies": {
2832
"@tailwindcss/postcss": "^4.1.11",
33+
"@testing-library/jest-dom": "^6.6.4",
2934
"@types/node": "^24.1.0",
3035
"@types/uuid": "^10.0.0",
3136
"@vitejs/plugin-vue": "^6.0.0",
37+
"@vue/test-utils": "^2.4.6",
3238
"@vue/tsconfig": "^0.7.0",
39+
"jsdom": "^26.1.0",
3340
"rollup-plugin-node-polyfills": "^0.2.1",
3441
"terser": "^5.43.1",
3542
"typescript": "~5.8.3",
36-
"vite": "^7.0.4",
43+
"vite": "^7.0.6",
3744
"vite-plugin-node-polyfills": "^0.24.0",
45+
"vitest": "^3.2.4",
3846
"vue-tsc": "^2.2.12"
3947
}
40-
}
48+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { mount } from '@vue/test-utils'
2+
import { describe, it, expect, vi } from 'vitest'
3+
import type { MockedFunction } from 'vitest'
4+
import AuthForm from '../components/AuthForm.vue'
5+
6+
vi.mock('../services/sendingEvent', () => ({
7+
sendingEvent: vi.fn(),
8+
}))
9+
10+
import { sendingEvent } from '../services/sendingEvent'
11+
import type { BaseAuth } from '../types/auth.interface'
12+
13+
14+
const maskStub = {
15+
beforeMount() {
16+
17+
}
18+
}
19+
20+
const mockedSendingEvent = sendingEvent as MockedFunction<typeof sendingEvent>
21+
22+
describe('AuthForm.vue', () => {
23+
const baseProps: BaseAuth = {
24+
type: 'login',
25+
inputs: [
26+
{ id: 'email', label: 'Email', type: 'email', required: true },
27+
{ id: 'password', label: 'Senha', type: 'password', required: true },
28+
],
29+
isLoading: false,
30+
}
31+
it('renderiza os inputs e botões corretamente', () => {
32+
const wrapper = mount(AuthForm, {
33+
props: baseProps,
34+
global: {
35+
directives: {
36+
mask: maskStub
37+
}
38+
}
39+
})
40+
41+
baseProps.inputs.forEach(input => {
42+
expect(wrapper.html()).toContain(input.label)
43+
expect(wrapper.find(`input#${input.id}`).exists()).toBe(true)
44+
})
45+
46+
const buttons = wrapper.findAllComponents({ name: 'Button' })
47+
expect(buttons.length).toBeGreaterThanOrEqual(2)
48+
49+
const enterButton = buttons.find(btn => btn.text().includes('Entrar'))
50+
expect(enterButton).toBeTruthy()
51+
})
52+
53+
54+
55+
it('chama sendingEvent no submit do formulário', async () => {
56+
const wrapper = mount(AuthForm, {
57+
props: baseProps,
58+
global: {
59+
directives: {
60+
mask: maskStub
61+
}
62+
}
63+
})
64+
65+
await wrapper.find('form').trigger('submit.prevent')
66+
67+
expect(mockedSendingEvent).toHaveBeenCalledTimes(1)
68+
69+
const callArg = mockedSendingEvent.mock.calls[0][0]
70+
expect(callArg.props).toMatchObject(baseProps)
71+
expect(typeof callArg.emit).toBe('function')
72+
expect(callArg.e).toBeDefined()
73+
})
74+
75+
it('emite evento register quando botão registrar é clicado (type=login)', async () => {
76+
const wrapper = mount(AuthForm, {
77+
props: baseProps,
78+
global: {
79+
directives: {
80+
mask: maskStub
81+
}
82+
}
83+
})
84+
85+
const registerButton = wrapper.findAllComponents({ name: 'Button' }).find(btn => btn.text() === 'Registrar')
86+
expect(registerButton).toBeTruthy()
87+
await registerButton?.trigger('click')
88+
89+
expect(wrapper.emitted()).toHaveProperty('register')
90+
})
91+
92+
it('emite evento login quando botão voltar é clicado (type=register)', async () => {
93+
const wrapper = mount(AuthForm, {
94+
props: {
95+
...baseProps,
96+
type: 'register',
97+
},
98+
global: {
99+
directives: {
100+
mask: maskStub
101+
}
102+
}
103+
})
104+
105+
const backButton = wrapper.findAllComponents({ name: 'Button' }).find(btn => btn.text() === 'Voltar')
106+
expect(backButton).toBeTruthy()
107+
await backButton?.trigger('click')
108+
109+
expect(wrapper.emitted()).toHaveProperty('login')
110+
})
111+
})
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { sendingEvent } from '../services/sendingEvent'
2+
import { describe, it, expect, vi, beforeEach } from 'vitest'
3+
4+
import { toast } from 'vue3-toastify'
5+
import type { BaseAuth } from '../types/auth.interface'
6+
7+
vi.mock('vue3-toastify', () => ({
8+
toast: {
9+
error: vi.fn(),
10+
},
11+
}))
12+
13+
describe('sendingEvent', () => {
14+
const mockEmit = vi.fn()
15+
16+
beforeEach(() => {
17+
vi.clearAllMocks()
18+
})
19+
20+
function createMockEvent(inputs: Record<string, string>) {
21+
const form = document.createElement('form')
22+
Object.entries(inputs).forEach(([name, value]) => {
23+
const input = document.createElement('input')
24+
input.id = name
25+
input.value = value
26+
if (name.toLowerCase().includes('password')) {
27+
input.type = 'password'
28+
} else if (name.toLowerCase().includes('document')) {
29+
input.type = 'text'
30+
} else {
31+
input.type = 'text'
32+
}
33+
34+
form.appendChild(input)
35+
})
36+
37+
return {
38+
preventDefault: vi.fn(),
39+
target: form,
40+
} as unknown as Event & { target: HTMLFormElement; preventDefault: () => void }
41+
}
42+
43+
const basePropsLogin: BaseAuth = {
44+
type: 'login',
45+
inputs: [
46+
{ id: 'document', label: 'Documento', type: 'text', required: true },
47+
{ id: 'password', label: 'Senha', type: 'password', required: true },
48+
],
49+
isLoading: false,
50+
}
51+
52+
const basePropsRegister: BaseAuth = {
53+
type: 'register',
54+
inputs: [
55+
{ id: 'name', label: 'Nome', type: 'text', required: true },
56+
{ id: 'document', label: 'Documento', type: 'text', required: true },
57+
{ id: 'password', label: 'Senha', type: 'password', required: true },
58+
{ id: 'passwordConfirmation', label: 'Confirmação de senha', type: 'password', required: true },
59+
],
60+
isLoading: false,
61+
}
62+
63+
it('deve impedir submissão e emitir evento login com dados válidos', () => {
64+
const e = createMockEvent({
65+
document: '12345678909',
66+
password: 'Password1!',
67+
})
68+
69+
sendingEvent({ e, props: basePropsLogin, emit: mockEmit })
70+
71+
expect(e.preventDefault).toHaveBeenCalled()
72+
expect(mockEmit).toHaveBeenCalledWith('submit', {
73+
document: '12345678909',
74+
password: 'Password1!',
75+
})
76+
})
77+
78+
it('deve exibir erro para CPF inválido', () => {
79+
const e = createMockEvent({
80+
document: 'invalid-cpf',
81+
password: 'Password1!',
82+
})
83+
84+
sendingEvent({ e, props: basePropsLogin, emit: mockEmit })
85+
86+
expect(toast.error).toHaveBeenCalledWith('Documento inválido')
87+
expect(mockEmit).not.toHaveBeenCalled()
88+
})
89+
90+
it('deve exibir erro para senha inválida', () => {
91+
const e = createMockEvent({
92+
document: '12345678909',
93+
password: '123',
94+
})
95+
96+
sendingEvent({ e, props: basePropsLogin, emit: mockEmit })
97+
98+
expect(toast.error).toHaveBeenCalledWith('Senha inválida')
99+
expect(mockEmit).not.toHaveBeenCalled()
100+
})
101+
102+
it('deve exibir erro para nome vazio no register', () => {
103+
const e = createMockEvent({
104+
name: ' ',
105+
document: '12345678909',
106+
password: 'Password1!',
107+
passwordConfirmation: 'Password1!',
108+
})
109+
110+
sendingEvent({ e, props: basePropsRegister, emit: mockEmit })
111+
112+
expect(toast.error).toHaveBeenCalledWith('Nome é obrigatório')
113+
expect(mockEmit).not.toHaveBeenCalled()
114+
})
115+
116+
it('deve exibir erro se não confirmar senha no register', () => {
117+
const e = createMockEvent({
118+
name: 'Leonardo',
119+
document: '12345678909',
120+
password: 'Password1!',
121+
passwordConfirmation: '',
122+
})
123+
124+
sendingEvent({ e, props: basePropsRegister, emit: mockEmit })
125+
126+
expect(toast.error).toHaveBeenCalledWith('Confirme a senha')
127+
expect(mockEmit).not.toHaveBeenCalled()
128+
})
129+
130+
131+
})

0 commit comments

Comments
 (0)