Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
fdodino committed Nov 16, 2024
0 parents commit 0001c7e
Show file tree
Hide file tree
Showing 21 changed files with 2,612 additions and 0 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Build
on:
push:
pull_request:
branches:
- master

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]

steps:
- uses: actions/checkout@v4

- name: Cache node modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Node ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

- name: Run tests & linter
run: |
yarn install
yarn run lint && npm run check
yarn run test:ci
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
21 changes: 21 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
node_modules

# Output
.output
.vercel
/.svelte-kit
/build

# OS
.DS_Store
Thumbs.db

# Env
.env
.env.*
!.env.example
!.env.test

# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
engine-strict=true
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Package Managers
package-lock.json
pnpm-lock.yaml
yarn.lock
17 changes: 17 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"useTabs": true,
"singleQuote": true,
"tabWidth": 2,
"semi": false,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte"
}
}
]
}
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"editor.formatOnSave": true,
"editor.tabSize": 2,
"eslint.validate": ["javascript", "javascriptreact", "svelte"],
}
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

# Conversor en Svelte

Este proyecto representa el clásico ejemplo del conversor de millas a kilómetros, generado con [`sv`](https://github.com/sveltejs/cli), con las siguientes configuraciones:

- Typescript
- ESlint + prettier + vitest
- agregamos plugins de testing library para tests de front
- y manejamos las dependencias con yarn (es mucho más rápido que usar npm)

## Implementación

- El input millas se define como number para evitar valores alfabéticos incorrectos
- También tiene un **binding** contra el estado (la runa `$.state`), llamado _miles_
- Como los kilómetros se definen en función de las millas, utilizamos la runa `$derived`. Esto automáticamente recalcula el valor cuando cambiamos las millas
- De yapa, utilizamos una función que muestra los kilómetros en formato coma decimal
- Como solo tenemos una página, ubicamos dentro de la carpeta `src/routes` el archivo `+page.svelte` que la define como página principal de nuestra SPA (single page application)

Lo bueno es que Svelte necesita de esos pocos conceptos dentro de nuestra página:

```sv
<script lang="ts">
import { convertMilesToKms } from '$lib'
const formattedNumber = (value: number) => value.toLocaleString('es');
let miles = $state(0)
let kilometers = $derived(formattedNumber(convertMilesToKms(miles)))
...
</script>
<div class="form">
...
<div class="row">
<input type='number' data-testid='millas' bind:value={miles} />
</div>
<div class="row">
<span data-testid="kilometers">{kilometers}</span>
```

### Botón reset

Para resetear el valor de las millas contamos con un botón al que le asociamos una función que cambia el estado de millas:

```sv
<script lang="ts">
...
const reset = () => { miles = 0 }
</script>
<button class="button secondary" data-testid="reset" onclick={reset}>Resetear</button>
```

## Tests

La propiedad `data-testid` nos sirve para encontrar fácilmente los elementos del DOM y probar

- que inicialmente el valor del input millas es 0
- que podemos convertir exitosamente de millas a kilómetros (y también verificamos que la conversión se haga respetando el locale castellano con la coma decimal)
- y por último probamos el botón de Reset

El lector puede ver los tests y su configuración con Testing Library.
52 changes: 52 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// eslint.config.cjs

import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
import eslintPluginSvelte from 'eslint-plugin-svelte'
import js from '@eslint/js'
import svelteParser from 'svelte-eslint-parser'
import tsEslint from 'typescript-eslint'
import tsParser from '@typescript-eslint/parser'

export default [
js.configs.recommended,
...tsEslint.configs.strict,
...eslintPluginSvelte.configs['flat/recommended'],
eslintPluginPrettierRecommended, // must be last to override conflicting rules.
{
rules: {
semi: ['warn', 'always'],
quotes: ['warn', 'single', { avoidEscape: true, allowTemplateLiterals: true }],
'semi': 'error',
'no-nested-ternary': 'error',
'linebreak-style': ['error', 'unix'],
'no-cond-assign': ['error', 'always'],
'no-console': 'error',
'@typescript-eslint/sort-type-constituents': 'error',
'sort-imports': [
'error',
{
ignoreCase: true,
ignoreDeclarationSort: false,
ignoreMemberSort: false,
memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
allowSeparatedGroups: true
}
]
}
},
{
files: ['**/*.svelte'],
languageOptions: {
parser: svelteParser,
parserOptions: {
parser: tsParser
}
},
rules: {
'svelte/no-target-blank': 'error',
'svelte/no-at-debug-tags': 'error',
'svelte/no-reactive-functions': 'error',
'svelte/no-reactive-literals': 'error'
}
}
]
40 changes: 40 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "eg-conversor-svelte",
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
"lint": "eslint && prettier --check .",
"lint:fix": "eslint && prettier --write .",
"test:unit": "vitest",
"test": "npm run test:unit -- --run",
"test:ci": "npm run test:unit -- --run --coverage"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/svelte": "^5.2.4",
"@testing-library/user-event": "^14.5.2",
"@types/eslint": "^9.6.0",
"eslint": "^9.7.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.36.0",
"globals": "^15.0.0",
"jsdom": "^25.0.1",
"prettier": "^3.3.2",
"prettier-plugin-svelte": "^3.2.6",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"typescript": "^5.0.0",
"typescript-eslint": "^8.0.0",
"vite": "^5.0.3",
"vitest": "^2.0.4"
}
}
13 changes: 13 additions & 0 deletions src/app.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}

export {}
18 changes: 18 additions & 0 deletions src/app.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter&display=swap" rel="stylesheet">
<link rel="icon" type="image/x-icon" href="favicon.ico">

%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>
42 changes: 42 additions & 0 deletions src/contador.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { describe, it, expect } from 'vitest'
import { fireEvent, render, screen } from '@testing-library/svelte'
import userEvent from '@testing-library/user-event'

import Conversor from './routes/+page.svelte'

describe('Conversor', () => {

it('should start with 0 miles & kilometers', () => {
render(Conversor)

const miles = screen.getByTestId('millas') as HTMLInputElement
expect(miles.value).to.equal('0')

const kilometers = screen.getByTestId('kilometers')
expect(+kilometers.innerHTML).to.equal(0)
})

it('should convert valid miles to kilometers to locale es', async () => {
render(Conversor)

const miles = screen.getByTestId('millas') as HTMLInputElement
await userEvent.type(miles, '100')
const kilometers = screen.getByTestId('kilometers')
expect(kilometers.innerHTML).to.equal('160,934')
})

it('reset puts miles value back to zero', async () => {
render(Conversor)

const miles = screen.getByTestId('millas') as HTMLInputElement
await userEvent.type(miles, '100')
const user = userEvent.setup()

await user.click(screen.getByTestId('reset'))

expect(miles.value).to.equal('0')
const kilometers = screen.getByTestId('kilometers')
expect(kilometers.innerHTML).to.equal('0')
})

})
2 changes: 2 additions & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// place files you want to import through the `$lib` alias in this folder.
export const convertMilesToKms = (millas: number) => millas * 1.609344
26 changes: 26 additions & 0 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script lang="ts">
import './styles.css'
import { convertMilesToKms } from '$lib'
const formattedNumber = (value: number) => value.toLocaleString('es');
let miles = $state(0)
let kilometers = $derived(formattedNumber(convertMilesToKms(miles)))
const reset = () => { miles = 0 }
</script>

<div class="form">
<h1>Conversor millas a kms</h1>
<div class="row">
<label for="millas" class="label">Millas</label>
<input type='number' data-testid='millas' bind:value={miles} />
</div>
<div class="row">
<label for="kilometros" class="label">Kilómetros</label>
<span data-testid="kilometers">{kilometers}</span>
</div>
<div class="row">
<button class="button secondary" data-testid="reset" onclick={reset}>Resetear</button>
</div>
</div>
Loading

0 comments on commit 0001c7e

Please sign in to comment.