Skip to content

Commit 1ebc1be

Browse files
author
Carlos Fernandez Cabrero
committed
Initial commit
0 parents  commit 1ebc1be

27 files changed

+477
-0
lines changed

.editorconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# EditorConfig is awesome: https://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
[*]
7+
indent_style = space
8+
indent_size = 2
9+
end_of_line = lf
10+
charset = utf-8
11+
trim_trailing_whitespace = false
12+
insert_final_newline = false

.eslintrc.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module.exports = {
2+
env: {
3+
browser: true,
4+
es2021: true
5+
},
6+
extends: [
7+
'plugin:cypress/recommended',
8+
'plugin:react/recommended',
9+
'standard'
10+
],
11+
parserOptions: {
12+
ecmaFeatures: {
13+
jsx: true
14+
},
15+
ecmaVersion: 'latest',
16+
sourceType: 'module'
17+
},
18+
plugins: ['react', 'cypress'],
19+
rules: {
20+
'react/react-in-jsx-scope': 'off'
21+
}
22+
}

.gitignore

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
package-lock.json
16+
17+
# Editor directories and files
18+
.vscode/*
19+
!.vscode/extensions.json
20+
.idea
21+
.DS_Store
22+
*.suo
23+
*.ntvs*
24+
*.njsproj
25+
*.sln
26+
*.sw?

.prettierrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"singleQuote": true,
3+
"trailingComma": "none",
4+
"semi": false
5+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { cleanup, render, screen } from '@testing-library/react'
2+
import userEvent from '@testing-library/user-event'
3+
import App from 'components/App'
4+
import { beforeEach, describe, expect, test, vi } from 'vitest'
5+
6+
const mockIncrement = vi.fn()
7+
const mockDecrement = vi.fn()
8+
const mockReset = vi.fn()
9+
10+
vi.mock('hooks/useCounter', () => ({
11+
useCounter: () => ({
12+
counterValue: 0,
13+
increment: mockIncrement,
14+
decrement: mockDecrement,
15+
reset: mockReset
16+
})
17+
}))
18+
19+
describe('Pruebas sobre el componente <App/>', () => {
20+
beforeEach(() => {
21+
cleanup()
22+
})
23+
24+
test('debe incrementar el valor del contador en 1 cuando se pulsa el botón de Incrementar', () => {
25+
render(<App />)
26+
27+
const button = screen.getByRole('button', { name: /increment/i })
28+
29+
userEvent.click(button)
30+
31+
expect(mockIncrement).toBeCalledTimes(1)
32+
expect(mockIncrement).toBeCalledWith(1)
33+
})
34+
35+
test('debe decrementar el valor del contador en 1 cuando se pulsa el botón de Decrement', () => {
36+
render(<App />)
37+
38+
const button = screen.getByRole('button', { name: /decrement/i })
39+
40+
userEvent.click(button)
41+
42+
expect(mockDecrement).toBeCalledTimes(1)
43+
expect(mockDecrement).toBeCalledWith(1)
44+
})
45+
46+
test('debe resetear el valor del contador a 0 cuando se pulsa el botón de Reset', () => {
47+
render(<App />)
48+
49+
const resetButton = screen.getByRole('button', { name: /reset/i })
50+
51+
userEvent.click(resetButton)
52+
53+
expect(mockReset).toBeCalledTimes(1)
54+
})
55+
})
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { render, screen } from '@testing-library/react'
2+
import App from 'components/App'
3+
import { describe, test, vi } from 'vitest'
4+
5+
vi.mock('components/CounterButton', () => ({
6+
__esModule: true,
7+
default: () => <></>
8+
}))
9+
10+
vi.mock('hooks/useCounter', () => ({
11+
useCounter: () => ({ counterValue: 0 })
12+
}))
13+
14+
describe('Pruebas sobre el componente <App/>', () => {
15+
test('debe mostrar por defecto el valor inicial pasado al hook useCounter', () => {
16+
render(<App/>)
17+
18+
screen.getByText(0)
19+
})
20+
})
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { randText } from '@ngneat/falso'
2+
import { render, screen } from '@testing-library/react'
3+
import userEvent from '@testing-library/user-event'
4+
import CounterButton from 'components/CounterButton'
5+
import { describe, expect, test, vi } from 'vitest'
6+
7+
describe('Pruebas sobre el componente <CounterButton/>', () => {
8+
test('debe llamar a la función que se le pasa en la props cuando se hace click', () => {
9+
const mockAction = vi.fn()
10+
const text = randText()
11+
12+
render(<CounterButton text={text} action={mockAction} />)
13+
14+
const button = screen.getByRole('button', { name: text })
15+
16+
userEvent.click(button)
17+
18+
expect(mockAction).toBeCalledTimes(1)
19+
})
20+
21+
test('debe mostrar el texto que recibe en las props', () => {
22+
const text = randText()
23+
24+
render(<CounterButton text={text} action={vi.fn()} />)
25+
26+
screen.getByText(text)
27+
})
28+
})

cypress.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"baseUrl": "http://localhost:3000"
3+
}

cypress/fixtures/example.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "Using fixtures to represent data",
3+
"email": "hello@cypress.io",
4+
"body": "Fixtures are a great way to mock data for responses to routes"
5+
}

cypress/integration/App.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
describe('Pruebas sobre la APP', () => {
2+
beforeEach(() => {
3+
cy.visit('/')
4+
})
5+
6+
it('debe mostrar por defecto el valor 0 en el contador', () => {
7+
cy.get('[data-test-id=counter-value]').should('have.text', '0')
8+
})
9+
10+
it('debe incrementar el valor en 1 cuando se pulsa el botón Increment', () => {
11+
cy.get('[data-test-id=btn-increment]').click()
12+
cy.get('[data-test-id=counter-value]').should('have.text', '1')
13+
})
14+
15+
it('debe decrementar el valor en 1 cuando se pulsa el botón Decrement', () => {
16+
cy.get('[data-test-id=btn-decrement]').click()
17+
cy.get('[data-test-id=counter-value]').should('have.text', '-1')
18+
})
19+
20+
it('debe resetear el valor en 1 cuando se pulsa el botón Reset', () => {
21+
cy.get('[data-test-id=btn-reset]').click()
22+
cy.get('[data-test-id=counter-value]').should('have.text', '0')
23+
})
24+
})

cypress/plugins/index.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/// <reference types="cypress" />
2+
// ***********************************************************
3+
// This example plugins/index.js can be used to load plugins
4+
//
5+
// You can change the location of this file or turn off loading
6+
// the plugins file with the 'pluginsFile' configuration option.
7+
//
8+
// You can read more here:
9+
// https://on.cypress.io/plugins-guide
10+
// ***********************************************************
11+
12+
// This function is called when a project is opened or re-opened (e.g. due to
13+
// the project's config changing)
14+
15+
/**
16+
* @type {Cypress.PluginConfig}
17+
*/
18+
// eslint-disable-next-line no-unused-vars
19+
module.exports = (on, config) => {
20+
// `on` is used to hook into various events Cypress emits
21+
// `config` is the resolved Cypress config
22+
}

cypress/support/commands.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// ***********************************************
2+
// This example commands.js shows you how to
3+
// create various custom commands and overwrite
4+
// existing commands.
5+
//
6+
// For more comprehensive examples of custom
7+
// commands please read more here:
8+
// https://on.cypress.io/custom-commands
9+
// ***********************************************
10+
//
11+
//
12+
// -- This is a parent command --
13+
// Cypress.Commands.add('login', (email, password) => { ... })
14+
//
15+
//
16+
// -- This is a child command --
17+
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
18+
//
19+
//
20+
// -- This is a dual command --
21+
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
22+
//
23+
//
24+
// -- This will overwrite an existing command --
25+
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

cypress/support/index.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// ***********************************************************
2+
// This example support/index.js is processed and
3+
// loaded automatically before your test files.
4+
//
5+
// This is a great place to put global configuration and
6+
// behavior that modifies Cypress.
7+
//
8+
// You can change the location of this file or turn off
9+
// automatically serving support files with the
10+
// 'supportFile' configuration option.
11+
//
12+
// You can read more here:
13+
// https://on.cypress.io/configuration
14+
// ***********************************************************
15+
16+
// Import commands.js using ES2015 syntax:
17+
import './commands'
18+
19+
// Alternatively you can use CommonJS syntax:
20+
// require('./commands')

cypress/videos/App.test.js.mp4

62.9 KB
Binary file not shown.

index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Vite App</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.jsx"></script>
12+
</body>
13+
</html>

jsconfig.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"compilerOptions": {
3+
"baseUrl": "src"
4+
}
5+
}

package.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "react-counter-app",
3+
"private": true,
4+
"scripts": {
5+
"dev": "vite",
6+
"build": "vite build",
7+
"test": "vitest",
8+
"cy:open": "cypress open",
9+
"cy:run": "cypress run",
10+
"preview": "vite preview"
11+
},
12+
"dependencies": {
13+
"react": "^17.0.2",
14+
"react-dom": "^17.0.2",
15+
"tailwindcss": "3.0.23"
16+
},
17+
"devDependencies": {
18+
"@ngneat/falso": "2.26.1",
19+
"@testing-library/react": "12.1.3",
20+
"@testing-library/react-hooks": "7.0.2",
21+
"@testing-library/user-event": "13.5.0",
22+
"@vitejs/plugin-react": "^1.0.7",
23+
"cypress": "9.5.0",
24+
"eslint": "^7.32.0",
25+
"eslint-config-standard": "^16.0.3",
26+
"eslint-plugin-cypress": "2.12.1",
27+
"eslint-plugin-import": "^2.25.4",
28+
"eslint-plugin-node": "^11.1.0",
29+
"eslint-plugin-promise": "^5.2.0",
30+
"eslint-plugin-react": "^7.28.0",
31+
"jsdom": "^19.0.0",
32+
"prop-types": "15.8.1",
33+
"vite": "^2.8.0",
34+
"vitest": "0.4.2"
35+
}
36+
}

postcss.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {}
5+
}
6+
}

src/components/App/index.jsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import CounterButton from 'components/CounterButton'
2+
import { useCounter } from 'hooks/useCounter'
3+
4+
const App = () => {
5+
const { counterValue, increment, decrement, reset } = useCounter(0)
6+
7+
function handleIncrement () {
8+
increment(1)
9+
}
10+
11+
function handleDecrement () {
12+
decrement(1)
13+
}
14+
15+
function handleReset () {
16+
reset()
17+
}
18+
19+
return (
20+
<div className="bg-gray-800 text-white h-screen px-6 pb-6 pt-40 text-center">
21+
<p className="text-4xl mb-12">Counter App</p>
22+
<p data-test-id="counter-value" className="text-8xl mb-8">
23+
{counterValue}
24+
</p>
25+
26+
<div className="flex items-center gap-6 justify-center">
27+
<CounterButton text="Increment" action={handleIncrement} />
28+
<CounterButton text="Reset" action={handleReset} />
29+
<CounterButton text="Decrement" action={handleDecrement} />
30+
</div>
31+
</div>
32+
)
33+
}
34+
35+
export default App

0 commit comments

Comments
 (0)