Este será el primer artículo de muchos donde ire documentando mi proceso de aprendizaje de Vue. Es cierto que ya he hecho alguna pequeña aplicación con Vue hace ya bastante tiempo. En esta serie de post que voy a empezar voy a retomarlo todo desde cero.
Creando un componente
Para crear una aplicación vue tenemos vue cli, un paquete global de npm que podemos instalar con:
npm install -g @vue/cli
Utilizando vue cli podemos crear proyectos partiendo de distintas boilerplates. La oficial es "webpack" ( vue init webpack my-project) pero podemos utilizar cualquier otra de github. En este repositorio hay muchas de ellas, yo voy a utilizar la oficial.
Lo que haré será crear un pequeño componente para dar una valoración. Lo llamaré rating-component.
vue create ratings-component
Una de las cosas que nos preguntará es el preset que queremos. Elegiremos default.
Abrimos el proyecto con nuestro ide (yo utilizo VSCode) y ejecutamos el servidor en modo desarrollo. Para esto yo prefiero utilizar la consola del VSCode.
cd rating-component && code .
Luego en la consola de VSCode (ábrela con cmd + shift + t):
npm run dev
Veremos nuestra aplicación funcionando en localhost:8080
Por último, podemos configurar VSCode para que fixee los errores al guardar. Para ello creamos una carpeta .vscode en la raiz del proyecto, dentro creamos un fichero settings.json y añadimos:
De esta forma ahora al guardar se fixeará nuestro codigo.
Vue dev tools
Para debugear correctamente, es recomendable instalar las vue.js dev tools en nuestro navegador. Puedes encontrarlo en las extensiones de Firefox, Chrome o Safari. Aquí te dejo el enlace para las de Chrome
Creando nuestro componente
Una de las características de Vue es que el HTML, el JS y el CSS se suelen definir en el mismo fichero (con extensión .vue). El contenido de nuestro fichero se divide en tres secciones:
Vamos a crear un fichero Ratings.vue dentro de src/components con este contenido. Simplemente mostrarermos un h1 que diga Ratings, y le damos un nombre al componente (Ratings):
Lo siguiente que haremos es instalar una libreria de iconos para mostrar estrellas rellenas o vacias en funcion de la valoración que vayamos a darle a nuestro componente ratings.
npm i vue-unicons
En main.js, importaremos la libreria y los iconos que queremos. En este caso queremos star y star monochrome.
import Vue from "vue";
import Unicon from "vue-unicons";
import { uniStar, uniStarMonochrome } from "vue-unicons/src/icons";
import App from "./App";
Unicon.add([uniStar, uniStarMonochrome]);
Vue.use(Unicon);
Vue.config.productionTip = false;
/* eslint-disable no-new */
new Vue({
el: "#app",
render: h => h(App)
});
Para quitarle el estilo de lista (los puntos), en el css indicaoms list-style: none:
<style>
ul {
list-style: none;
}
</style>
Ahora mismo deberiamos ver dos estrellas naranjas (una rellenada y otra sin rellenar). En nuestro componente, vamos a permitir valorar del 1 al 10, por lo que vamos a añadir 10 estrellas vacias de momento. Utilizaremos un v-for para mostrar 10 estrellas vacias:
Aún podemos hacerlo mejor. Definiremos dos variables para controlar nuestro componente.
stars: numero de estrellas seleccionadas
maxStars: numero máximo de estrellas que podemos utilizar.
Y reemplazamos el numero 10 del v-for por maxStars.
Queremos que nuestras estrellas se muestren de forma horizontal. Vamos a hacerlo modificando la lista en los estilos:
<style>
ul {
list-style: none;
}
li {
float: left;
}
</style>
Primera funcionalidad. Estrellas seleccionadas
Lo primero que vamos a hacer es que se muestren tantas estrellas rellenas como indiquemos en la variable stars (que ahora mismo es igual a 0.
Vamos a ponerle un valor de 6.
A continuación, en nuestro v-for sabemos que la n es el numero en el bucle. Queremos que si el numero de stars es 6, se muestren 6 rellenas.
En unicons, la forma de cambiar de rellena a no rellena es utilizando la propiedad icon-style. Si es monochrome, aparece rellena y si es line (por defecto es line) aparece solo el borde. Lo que vamos a hacer es bindear la propiedad icon-style para que interprete el resultado en base a una logica.
Puedes comprobar que el código funciona correctamente.
Añadiendo un texto descriptivo
Lo siguiente que queremos hacer es mostrar un texto que indique la valoración, en nuestro caso: n de maxStars. Colocamos lo siguiente a continuación del cierre de
<span>{{ stars }} de {{ maxStars }}</span>
Podemos hacerlo mejor usarlo una computed propierty. Este tipo de elementos son valores que se actualizan cuando otros cambian. Vamos a crear una computed property llamada counter:
computed: {
counter() {
return `${this.stars} de ${this.maxStars}`
}
}
Y reemplazamos el texto del por:
<span>{{ counter }}</span>
Vemos que se sigue viendo igual. En cuanto al estilo, vamos a hacer unos cuantos retoques. Modificamos nuestro div para que muestre el texto debajo de la lista. Por supuesto podríamos / deberiamos crear clases en vez de dar estilos a los elementos directamente, pero no es el objetivo de este post:
<style>
div {
display: flex;
flex-direction: column;
}
ul {
list-style: none;
}
li {
float: left;
}
</style>
Vemos que las estrellas salen muy separadas respecto al texto. Esto es porque las listas tienen un margen y un padding por defecto. Vamos a actualizar li y ul con margin: 0 y padding: 0:
ul {
list-style: none;
margin: 0;
padding: 0;
}
li {
float: left;
margin: 0;
padding: 0;
}
Modificando la valoracion
Queremos que cuando el usuario clique en una estrella, la valoración se modifique a ese valor. Por ejemplo si clica en la estrella número 2, la valoración debe ser 2. Para ello, vamos a utilizar el evento @click y llamaremos una funcion que se llame setRate a la que le pasaremos la estrella en el array que se ha clicado:
<li v-for="n in maxStars" :key="n" **@click="setRate(n)"**>
Para modificar las stars de data, necesitamos crear el método setRate y actualizar el valor utilizando lo que recibimos por parámetro:
methods: {
setRate(star) {
this.stars = star;
}
}
Ahora puedes comprobar que cada vez que clicamos en una estrella diferente, el rate cambia.
Haciendo el componente reutilizable
hasta ahora hemos definido internamente el valor inicial y el maximo valor que pueden dar los usuarios. Para que el componente sea reutilizable, estos valores deben de poder recibirse desde un componente padre. Es decir, en unas aplicaciones querremos un rating del 1 al 5, en otras del 1 al 10, etc.
Para ello vamos a definir dos props: selectedStars y maxStars
props: ["selectedStars", "maxStars"],
maxStars no va a cambiar, por lo que podemos usarlo directamente pero en el caso de selectedStars, lo que haremos será inicializar nuestra variable stars dentro de data, al valor que recibimos por parámetro:
data() {
return {
stars: this.selectedStars
}
},
Por último, para pasarle las props a nuestro componente utilizamos el shorthand de v-bind (dos puntos) en App.vue:
<Ratings :maxStars="10" :selectedStars="3" />
Testing. Configurando el entorno
Para testear nuestro componente vamos a moverlo a una carpeta.
Creamos una carpeta Ratings dentro de components y movemos nuestro componente ahi. Actualizamos el import en App.vue y procedemos a crear nuestro fichero Ratings.test.js.
Si empezaramos a escribir nuestro terst, veríamos una serie de warnings indicandote que las palabras describe and it no están definidas. Es un error de eslint. Para solucionarlo añadiremos jest a los entornos de eslint (.eslintrc):
Para ejecutar los test, recuerda que has de ejecutar el comando jest. Puedes crearte una serie de scripts en el package.json para ejecutar jest en modo watch y jest con coverage:
Cuando lanzes el comando coverage, aparte de ver la cobertura por consola se te crearán los ficheros correspondientes en la carpeta coverage del proyecto.
Creando nuestro test
Vamos a importar render, screen y fireEvent en nuestro fichero de test, las depedencias de Unicon para poder testear el componetne con iconos y el componente que vamos a testear (Ratings). Inicializamos Unicon como hicimos en el main.js:
import { render, screen, fireEvent } from "@testing-library/vue";
import Vue from "vue";
import Unicon from "vue-unicons";
import { uniStar, uniStarMonochrome } from "vue-unicons/src/icons";
import Ratings from "./Ratings";
Unicon.add([uniStar, uniStarMonochrome]);
Vue.use(Unicon);
Antes de continuar, vamos a añadir un par de propiedades a nuestro Ratings.vue para poder acceder a los iconos:
El primer test que vamos a hacer es comprobar que se renderizan tantas estrellas como indicamos con la propiedad maxStars:
describe("Ratings.vue", () => {
it("should render as many stars as we indicate using the maxStars property", () => {
render(Ratings, {
props: {
maxStars: 10
}
});
expect(screen.queryAllByRole("rating-icon").length).toBe(10);
});
});
El segundo caso será comprobar que hay tantas estrellas filled como indicamos en selectedStars:
it("should render as many filled stars as we indicate using the selectedStars property", () => {
render(Ratings, {
props: {
maxStars: 10,
selectedStars: 6
}
});
expect(screen.queryAllByTestId("filled-star").length).toBe(6);
expect(screen.queryAllByTestId("empty-star").length).toBe(4);
});
En el tercer caso, comprobaremos que el texto aparece y es correcto:
it("should show a text with the amount of selected stars", () => {
render(Ratings, {
props: {
maxStars: 10,
selectedStars: 6
}
});
expect(screen.getByText("6 de 10")).toBeTruthy();
});
Y en el último comprobaremos que al seleccionar otro rating, el numero de estrellas rellenadas y el numero de estrellas vacias cambian, así como el texto:
it("should change the rating when select another one", async () => {
render(Ratings, {
props: {
maxStars: 10,
selectedStars: 6
}
});
const icons = screen.queryAllByRole("rating-icon");
expect(icons.length).toBe(10);
expect(screen.queryAllByTestId("filled-star").length).toBe(6);
expect(screen.queryAllByTestId("empty-star").length).toBe(4);
expect(screen.getByText("6 de 10")).toBeTruthy();
await fireEvent.click(screen.getByTestId("icon-2"));
expect(screen.queryAllByTestId("filled-star").length).toBe(2);
expect(screen.queryAllByTestId("empty-star").length).toBe(8);
expect(screen.getByText("2 de 10")).toBeTruthy();
});
Y con esto ya tendríamos un 100% de cobertura en nuestro componente.
Espero que te sea de utilidad y si tienes sugerencias o mejores enfoques no dudes en comentar, este articulo es basado en mi aprendizaje de Vue.
date: "2020-06-27" img: "https://res.cloudinary.com/ivanbtrujillo/image/upload/v1593276419/C2_BFQue_CC_81-debe-tener-un-profesional-especialista-en-Vue_qmljgs.png" summary: Crearé un componente desde cero con Vue.JS y lo testearé con jest y vue-testing-library
Este será el primer artículo de muchos donde ire documentando mi proceso de aprendizaje de Vue. Es cierto que ya he hecho alguna pequeña aplicación con Vue hace ya bastante tiempo. En esta serie de post que voy a empezar voy a retomarlo todo desde cero.
Creando un componente
Para crear una aplicación vue tenemos vue cli, un paquete global de npm que podemos instalar con:
Utilizando vue cli podemos crear proyectos partiendo de distintas boilerplates. La oficial es "webpack" ( vue init webpack my-project) pero podemos utilizar cualquier otra de github. En este repositorio hay muchas de ellas, yo voy a utilizar la oficial.
Lo que haré será crear un pequeño componente para dar una valoración. Lo llamaré rating-component.
Una de las cosas que nos preguntará es el preset que queremos. Elegiremos default.
Abrimos el proyecto con nuestro ide (yo utilizo VSCode) y ejecutamos el servidor en modo desarrollo. Para esto yo prefiero utilizar la consola del VSCode.
Luego en la consola de VSCode (ábrela con cmd + shift + t):
Veremos nuestra aplicación funcionando en localhost:8080
Eslint
Instalamos los siguientes paquetes:
Creamos un fichero .eslintrc.js y añadimos:
Por último, podemos configurar VSCode para que fixee los errores al guardar. Para ello creamos una carpeta .vscode en la raiz del proyecto, dentro creamos un fichero settings.json y añadimos:
De esta forma ahora al guardar se fixeará nuestro codigo.
Vue dev tools
Para debugear correctamente, es recomendable instalar las vue.js dev tools en nuestro navegador. Puedes encontrarlo en las extensiones de Firefox, Chrome o Safari. Aquí te dejo el enlace para las de Chrome
Creando nuestro componente
Una de las características de Vue es que el HTML, el JS y el CSS se suelen definir en el mismo fichero (con extensión .vue). El contenido de nuestro fichero se divide en tres secciones:
Vamos a crear un fichero Ratings.vue dentro de src/components con este contenido. Simplemente mostrarermos un h1 que diga Ratings, y le damos un nombre al componente (Ratings):
Lo siguiente que haremos es ir a App.vue e importarlo dentro de :
Lo declaramos como componente:
Y lo usamos:
Verás que ahora aparece el texto Ratings en la web.
Ahora que ya tenemos nuestro componente funcionando, vamos a limpiar App.vue para quedarnos solo con nuestro componente Ratings:
Lo siguiente que haremos es instalar una libreria de iconos para mostrar estrellas rellenas o vacias en funcion de la valoración que vayamos a darle a nuestro componente ratings.
En main.js, importaremos la libreria y los iconos que queremos. En este caso queremos star y star monochrome.
Ahora en Ratings, utilizamos estos dos iconos:
Para quitarle el estilo de lista (los puntos), en el css indicaoms list-style: none:
Ahora mismo deberiamos ver dos estrellas naranjas (una rellenada y otra sin rellenar). En nuestro componente, vamos a permitir valorar del 1 al 10, por lo que vamos a añadir 10 estrellas vacias de momento. Utilizaremos un v-for para mostrar 10 estrellas vacias:
Aún podemos hacerlo mejor. Definiremos dos variables para controlar nuestro componente. stars: numero de estrellas seleccionadas maxStars: numero máximo de estrellas que podemos utilizar.
Para ello utilizaremos la propiedad data:
Y reemplazamos el numero 10 del v-for por maxStars.
Queremos que nuestras estrellas se muestren de forma horizontal. Vamos a hacerlo modificando la lista en los estilos:
Primera funcionalidad. Estrellas seleccionadas
Lo primero que vamos a hacer es que se muestren tantas estrellas rellenas como indiquemos en la variable stars (que ahora mismo es igual a 0.
Vamos a ponerle un valor de 6.
A continuación, en nuestro v-for sabemos que la n es el numero en el bucle. Queremos que si el numero de stars es 6, se muestren 6 rellenas.
En unicons, la forma de cambiar de rellena a no rellena es utilizando la propiedad icon-style. Si es monochrome, aparece rellena y si es line (por defecto es line) aparece solo el borde. Lo que vamos a hacer es bindear la propiedad icon-style para que interprete el resultado en base a una logica.
Si n (la estrella en el array) es menor o igual que el numero de estrellas, indicamos que sea monochrome y si no, sera line.
Es decir, si stars vale 6, las seis primeras estrellas seran monochrome y las cuatro útlimas seran line.
El v-bind se puede abreviar poniendo únicamente los dos puntos:
Puedes comprobar que el código funciona correctamente.
Añadiendo un texto descriptivo
Lo siguiente que queremos hacer es mostrar un texto que indique la valoración, en nuestro caso: n de maxStars. Colocamos lo siguiente a continuación del cierre de
Podemos hacerlo mejor usarlo una computed propierty. Este tipo de elementos son valores que se actualizan cuando otros cambian. Vamos a crear una computed property llamada counter:
Y reemplazamos el texto del por:
Vemos que se sigue viendo igual. En cuanto al estilo, vamos a hacer unos cuantos retoques. Modificamos nuestro div para que muestre el texto debajo de la lista. Por supuesto podríamos / deberiamos crear clases en vez de dar estilos a los elementos directamente, pero no es el objetivo de este post:
Vemos que las estrellas salen muy separadas respecto al texto. Esto es porque las listas tienen un margen y un padding por defecto. Vamos a actualizar li y ul con margin: 0 y padding: 0:
Modificando la valoracion
Queremos que cuando el usuario clique en una estrella, la valoración se modifique a ese valor. Por ejemplo si clica en la estrella número 2, la valoración debe ser 2. Para ello, vamos a utilizar el evento @click y llamaremos una funcion que se llame setRate a la que le pasaremos la estrella en el array que se ha clicado:
Para modificar las stars de data, necesitamos crear el método setRate y actualizar el valor utilizando lo que recibimos por parámetro:
Ahora puedes comprobar que cada vez que clicamos en una estrella diferente, el rate cambia.
Haciendo el componente reutilizable
hasta ahora hemos definido internamente el valor inicial y el maximo valor que pueden dar los usuarios. Para que el componente sea reutilizable, estos valores deben de poder recibirse desde un componente padre. Es decir, en unas aplicaciones querremos un rating del 1 al 5, en otras del 1 al 10, etc.
Para ello vamos a definir dos props: selectedStars y maxStars
maxStars no va a cambiar, por lo que podemos usarlo directamente pero en el caso de selectedStars, lo que haremos será inicializar nuestra variable stars dentro de data, al valor que recibimos por parámetro:
Por último, para pasarle las props a nuestro componente utilizamos el shorthand de v-bind (dos puntos) en App.vue:
Testing. Configurando el entorno
Para testear nuestro componente vamos a moverlo a una carpeta.
Creamos una carpeta Ratings dentro de components y movemos nuestro componente ahi. Actualizamos el import en App.vue y procedemos a crear nuestro fichero Ratings.test.js.
Si empezaramos a escribir nuestro terst, veríamos una serie de warnings indicandote que las palabras describe and it no están definidas. Es un error de eslint. Para solucionarlo añadiremos jest a los entornos de eslint (.eslintrc):
Ahora instalaremos Jest y vue-testing-library para empezar a testear.
Vue testing library funciona igual que la version de react, por lo que si estas acostumbrad@ a react testing library, te será muy familiar.
Por último, nos queda configurar Jest. Creamos un fichero jest.config.js en el root de nuestro proyecto con la siguiente configuración:
Para ejecutar los test, recuerda que has de ejecutar el comando jest. Puedes crearte una serie de scripts en el package.json para ejecutar jest en modo watch y jest con coverage:
Cuando lanzes el comando coverage, aparte de ver la cobertura por consola se te crearán los ficheros correspondientes en la carpeta coverage del proyecto.
Creando nuestro test
Vamos a importar render, screen y fireEvent en nuestro fichero de test, las depedencias de Unicon para poder testear el componetne con iconos y el componente que vamos a testear (Ratings). Inicializamos Unicon como hicimos en el main.js:
Antes de continuar, vamos a añadir un par de propiedades a nuestro Ratings.vue para poder acceder a los iconos:
El primer test que vamos a hacer es comprobar que se renderizan tantas estrellas como indicamos con la propiedad maxStars:
El segundo caso será comprobar que hay tantas estrellas filled como indicamos en selectedStars:
En el tercer caso, comprobaremos que el texto aparece y es correcto:
Y en el último comprobaremos que al seleccionar otro rating, el numero de estrellas rellenadas y el numero de estrellas vacias cambian, así como el texto:
Y con esto ya tendríamos un 100% de cobertura en nuestro componente.
Espero que te sea de utilidad y si tienes sugerencias o mejores enfoques no dudes en comentar, este articulo es basado en mi aprendizaje de Vue.
¡Un saludo!