Bruno Predot

Merge branch 'feature/test' into develop

  1 +0.3.1:
  2 +- ajout fichier de test MovieGender.spec.ts.
  3 +- ajout fichier de test ScoreAndVote.spec.ts.
  4 +
1 0.3.0: 5 0.3.0:
2 - Installation vuelidate et vuelidate/validator. 6 - Installation vuelidate et vuelidate/validator.
3 - Ajout composant SkeletonMovieDetailLoader. 7 - Ajout composant SkeletonMovieDetailLoader.
@@ -26,7 +26,8 @@ watch( @@ -26,7 +26,8 @@ watch(
26 }); 26 });
27 } 27 }
28 }); 28 });
29 - }, { immediate: true } 29 + },
  30 + { immediate: true },
30 ); 31 );
31 //#endregion 32 //#endregion
32 </script> 33 </script>
@@ -105,7 +105,7 @@ function handleMessageEvent(event: string) { @@ -105,7 +105,7 @@ function handleMessageEvent(event: string) {
105 @blur="v$.rating.$touch" 105 @blur="v$.rating.$touch"
106 @input="v$.rating.$touch" 106 @input="v$.rating.$touch"
107 /> 107 />
108 -<!-- <pre>{{ errormessages }}</pre>--> 108 + <!-- <pre>{{ errormessages }}</pre>-->
109 <ui-components-tiny-mce-field-editor 109 <ui-components-tiny-mce-field-editor
110 :error-message="v$?.message?.$errors[0]?.$message ? (v$.message.$errors[0].$message as string) : ''" 110 :error-message="v$?.message?.$errors[0]?.$message ? (v$.message.$errors[0].$message as string) : ''"
111 :model-value="formData.message" 111 :model-value="formData.message"
1 -import { describe, it, expect } from 'vitest' 1 +import { describe, expect, it } from "vitest";
2 -import { mount } from '@vue/test-utils' 2 +import { mount } from "@vue/test-utils";
3 3
4 -import HelloWorld from './HelloWorld.vue' 4 +import HelloWorld from "./HelloWorld.vue";
5 5
6 -describe('HelloWorld', () => { 6 +describe("HelloWorld", () => {
7 - it('component renders Hello world properly', () => { 7 + it("component renders Hello world properly", () => {
8 - const wrapper = mount(HelloWorld) 8 + const wrapper = mount(HelloWorld);
9 - expect(wrapper.text()).toContain('Hello world') 9 + expect(wrapper.text()).toContain("Hello world");
10 - }) 10 + });
11 -}) 11 +});
1 -<script setup lang="ts"> 1 +<script setup lang="ts"></script>
2 -  
3 -</script>  
4 2
5 <template> 3 <template>
6 <p>Hello world</p> 4 <p>Hello world</p>
7 </template> 5 </template>
8 6
9 -<style scoped lang="scss"> 7 +<style scoped lang="scss"></style>
10 -  
11 -</style>  
@@ -22,4 +22,4 @@ defineProps({ @@ -22,4 +22,4 @@ defineProps({
22 </section> 22 </section>
23 </template> 23 </template>
24 24
25 -<style scoped></style> 25 +<style scoped></style>
@@ -20,12 +20,7 @@ defineProps({ @@ -20,12 +20,7 @@ defineProps({
20 <template> 20 <template>
21 <section class="w-full md:w-1/3 lg:w-1/4"> 21 <section class="w-full md:w-1/3 lg:w-1/4">
22 <div class="rounded-lg overflow-hidden shadow-lg bg-gray-800"> 22 <div class="rounded-lg overflow-hidden shadow-lg bg-gray-800">
23 - <v-img 23 + <v-img v-if="src" :alt="title" :src="`https://image.tmdb.org/t/p/w500${src}`" class="w-full h-auto" />
24 - v-if="src"  
25 - :alt="title"  
26 - :src="`https://image.tmdb.org/t/p/w500${src}`"  
27 - class="w-full h-auto"  
28 - />  
29 <div v-else class="aspect-[2/3] bg-gray-700 flex items-center justify-center"> 24 <div v-else class="aspect-[2/3] bg-gray-700 flex items-center justify-center">
30 <FilmIcon :size="64" class="text-gray-500" /> 25 <FilmIcon :size="64" class="text-gray-500" />
31 </div> 26 </div>
@@ -33,6 +28,4 @@ defineProps({ @@ -33,6 +28,4 @@ defineProps({
33 </section> 28 </section>
34 </template> 29 </template>
35 30
36 -<style scoped> 31 +<style scoped></style>
37 -  
38 -</style>  
@@ -6,7 +6,7 @@ import { useDebounceFn } from "@vueuse/core"; @@ -6,7 +6,7 @@ import { useDebounceFn } from "@vueuse/core";
6 //#endregion 6 //#endregion
7 7
8 //#region --Emits--. 8 //#region --Emits--.
9 -const emit = defineEmits(['event:search', 'event:clear_search']); 9 +const emit = defineEmits(["event:search", "event:clear_search"]);
10 //#endregion 10 //#endregion
11 11
12 //#region --Props--. 12 //#region --Props--.
@@ -29,12 +29,12 @@ const searchQuery = ref(""); @@ -29,12 +29,12 @@ const searchQuery = ref("");
29 * Debounced function 29 * Debounced function
30 */ 30 */
31 const handleSearchEvent = useDebounceFn(() => { 31 const handleSearchEvent = useDebounceFn(() => {
32 - emit('event:search', searchQuery.value); 32 + emit("event:search", searchQuery.value);
33 }, 500); 33 }, 500);
34 34
35 function handleClearSearchEvent() { 35 function handleClearSearchEvent() {
36 - searchQuery.value = ''; 36 + searchQuery.value = "";
37 - emit('event:clear_search') 37 + emit("event:clear_search");
38 } 38 }
39 //#endregion 39 //#endregion
40 </script> 40 </script>
@@ -29,4 +29,4 @@ defineProps({ @@ -29,4 +29,4 @@ defineProps({
29 </section> 29 </section>
30 </template> 30 </template>
31 31
32 -<style scoped></style> 32 +<style scoped></style>
1 -<script setup lang="ts"> 1 +<script setup lang="ts"></script>
2 -  
3 -</script>  
4 2
5 <template> 3 <template>
6 <v-container class="bg-gray-900"> 4 <v-container class="bg-gray-900">
7 - <v-row class="bg-gray-900" > 5 + <v-row class="bg-gray-900">
8 <v-col cols="12" sm="4"> 6 <v-col cols="12" sm="4">
9 <v-skeleton-loader 7 <v-skeleton-loader
10 class="mx-auto border bg-gray-800" 8 class="mx-auto border bg-gray-800"
@@ -14,7 +12,7 @@ @@ -14,7 +12,7 @@
14 type="paragraph, image" 12 type="paragraph, image"
15 /> 13 />
16 </v-col> 14 </v-col>
17 - <v-col cols="12" sm="8"> 15 + <v-col cols="12" sm="8">
18 <v-skeleton-loader 16 <v-skeleton-loader
19 class="mx-auto mt-10" 17 class="mx-auto mt-10"
20 color="#1f2937" 18 color="#1f2937"
@@ -27,6 +25,4 @@ @@ -27,6 +25,4 @@
27 </v-container> 25 </v-container>
28 </template> 26 </template>
29 27
30 -<style scoped> 28 +<style scoped></style>
31 -  
32 -</style>  
1 { 1 {
2 "name": "nuxt-app", 2 "name": "nuxt-app",
3 - "version": "0.3.0", 3 + "version": "0.3.1",
4 "lockfileVersion": 3, 4 "lockfileVersion": 3,
5 "requires": true, 5 "requires": true,
6 "packages": { 6 "packages": {
7 "": { 7 "": {
8 "name": "nuxt-app", 8 "name": "nuxt-app",
9 - "version": "0.3.0", 9 + "version": "0.3.1",
10 "hasInstallScript": true, 10 "hasInstallScript": true,
11 "dependencies": { 11 "dependencies": {
12 "@nuxt/eslint": "^1.3.0", 12 "@nuxt/eslint": "^1.3.0",
1 { 1 {
2 "name": "nuxt-app", 2 "name": "nuxt-app",
3 - "version": "0.3.0", 3 + "version": "0.3.1",
4 "private": true, 4 "private": true,
5 "type": "module", 5 "type": "module",
6 "scripts": { 6 "scripts": {
  1 +//#region --Import--.
  2 +import { describe, expect, it } from "vitest";
  3 +import { mount } from "@vue/test-utils";
  4 +import MovieGender from "../../components/details/MovieGender.vue";
  5 +import type { Genre } from "~/interfaces/movie";
  6 +//#endregion
  7 +
  8 +describe("MovieGender", () => {
  9 + it("affiche correctement les genres", () => {
  10 + // Données de test.
  11 + const genres: Genre[] = [
  12 + { id: 1, name: "Action" },
  13 + { id: 2, name: "Comédie" },
  14 + { id: 3, name: "Drame" },
  15 + ];
  16 + // Monter le composant avec sa props.
  17 + const wrapper = mount(MovieGender, {
  18 + props: {
  19 + genres,
  20 + },
  21 + });
  22 + // Vérifier que tous les genres sont affichés.
  23 + const spanElements = wrapper.findAll("span");
  24 + // spanElements.length doit être égal aux données de test (genres.length).
  25 + expect(spanElements.length).toBe(genres.length);
  26 +
  27 + // Vérifier le contenu affiché de chaque élément.
  28 + spanElements.forEach((spanElement, index) => {
  29 + // Sur chaque itération d'élément (span), vérification que le nom du genre correspondant soit bien affiché.
  30 + expect(spanElement.text()).toContain(genres[index].name);
  31 + expect(spanElement.text()).toBe(genres[index].name);
  32 + });
  33 + });
  34 +
  35 + it("affiche correctement un seul genre", () => {
  36 + // Données de test.
  37 + const genres: Genre[] = [{ id: 1, name: "Horreur" }];
  38 + // Monter le composant avec sa props.
  39 + const wrapper = mount(MovieGender, {
  40 + props: {
  41 + genres,
  42 + },
  43 + });
  44 + // Vérifier que le genre soit affiché.
  45 + const spanElements = wrapper.findAll("span");
  46 + // spanElements.length doit être égal aux données de test (ici 1).
  47 + expect(spanElements.length).toBe(genres.length);
  48 +
  49 + // Vérifier le contenu affiché de l'élément.
  50 + expect(spanElements[0].text()).toContain(genres[0].name);
  51 + expect(spanElements[0].text()).toBe(genres[0].name);
  52 + });
  53 +
  54 + it("ne rend aucun élément span quand la liste est vide", () => {
  55 + // Données de test volontairement vide.
  56 + const genres: Genre[] = [];
  57 + // Monter le composant avec sa props.
  58 + const wrapper = mount(MovieGender, {
  59 + props: {
  60 + genres,
  61 + },
  62 + });
  63 + // spanElements.length doit être égal à 0.
  64 + const spanElements = wrapper.findAll("span");
  65 + expect(spanElements.length).toBe(0);
  66 + });
  67 +});
  1 +//#region --Import--.
  2 +import { describe, expect, it } from "vitest";
  3 +import { mount } from "@vue/test-utils";
  4 +import ScoreAndVote from "../../components/details/ScoreAndVote.vue";
  5 +//#endregion
  6 +
  7 +describe("ScoreAndVote", () => {
  8 + it("affiche correctement le score", () => {
  9 + // Monter le composant avec ses props.
  10 + const wrapper = mount(ScoreAndVote, {
  11 + props: {
  12 + score: 7,
  13 + nbVote: 100,
  14 + },
  15 + });
  16 + // Trouver l'élément qui contient le score.
  17 + const scoreElement = wrapper.find(".bg-primary");
  18 + // Vérifier que le score est affiché correctement.
  19 + expect(scoreElement.text()).contain("7");
  20 + });
  21 +
  22 + it("affiche exactement le score avec une décimale", () => {
  23 + // Monter le composant avec ses props.
  24 + const wrapper = mount(ScoreAndVote, {
  25 + props: {
  26 + score: 8.5,
  27 + nbVote: 100,
  28 + },
  29 + });
  30 + // Trouver l'élément qui contient le score.
  31 + const scoreElement = wrapper.find(".bg-primary");
  32 + // Vérifier que le score est affiché correctement.
  33 + expect(scoreElement.text()).toBe("8.5");
  34 + });
  35 +
  36 + it("arrondit correctement le score à une décimale", () => {
  37 + // Tester avec un score qui a plus d'une décimale.
  38 + const wrapper = mount(ScoreAndVote, {
  39 + props: {
  40 + score: 7.654,
  41 + nbVote: 100,
  42 + },
  43 + });
  44 + // Trouver l'élément qui contient le score.
  45 + const scoreElement = wrapper.find(".bg-primary");
  46 + // Arrondi à une décimale.
  47 + expect(scoreElement.text()).toBe("7.7");
  48 + });
  49 +
  50 + it("affiche correctement le paragraphe", () => {
  51 + // Monter le composant avec ses props.
  52 + const wrapper = mount(ScoreAndVote, {
  53 + props: {
  54 + score: 7.654,
  55 + nbVote: 100,
  56 + },
  57 + });
  58 + // Trouver l'élément qui contient le paragraphe.
  59 + const paragrapheElement = wrapper.find("p");
  60 + expect(paragrapheElement.text()).toBe("Note TMDB");
  61 + });
  62 +
  63 + it("affiche correctement le nombre de vote sans formatage si inférieur à 1000", () => {
  64 + // Monter le composant avec ses props.
  65 + const wrapper = mount(ScoreAndVote, {
  66 + props: {
  67 + score: 7.654,
  68 + nbVote: 855,
  69 + },
  70 + });
  71 + // Trouver l'élément qui contient le nombre de votes.
  72 + const voteElement = wrapper.find("div");
  73 + // Vérifier si la div contient le nombre.
  74 + expect(voteElement.text()).toContain("855");
  75 + // Vérifier si le texte de la div correspond exactement.
  76 + expect(voteElement.text()).toBe("855 votes");
  77 + });
  78 +
  79 + it("affiche correctement le nombre de vote formaté en 'k' si supérieur ou égal à 1000", () => {
  80 + // Monter le composant avec ses props.
  81 + const wrapper = mount(ScoreAndVote, {
  82 + props: {
  83 + score: 9,
  84 + nbVote: 1477,
  85 + },
  86 + });
  87 + // Trouver l'élément qui contient le nombre de votes.
  88 + const voteElement = wrapper.find("div");
  89 + // Vérifier si la div contient le nombre.
  90 + expect(voteElement.text()).toContain("1.5");
  91 + // Vérifier si le texte de la div correspond exactement.
  92 + expect(voteElement.text()).toBe("1.5k votes");
  93 + });
  94 +});
@@ -5,7 +5,7 @@ export default { @@ -5,7 +5,7 @@ export default {
5 plugins: [vue()], 5 plugins: [vue()],
6 test: { 6 test: {
7 globals: true, 7 globals: true,
8 - environment: "jsdom", 8 + environment: "happy-dom",
9 // Additional test configurations can be added here 9 // Additional test configurations can be added here
10 }, 10 },
11 } 11 }
1 import { defineVitestConfig } from '@nuxt/test-utils/config' 1 import { defineVitestConfig } from '@nuxt/test-utils/config'
  2 +import vue from '@vitejs/plugin-vue'
  3 +import { fileURLToPath } from 'node:url'
2 4
3 export default defineVitestConfig({ 5 export default defineVitestConfig({
4 /** 6 /**
@@ -7,15 +9,30 @@ export default defineVitestConfig({ @@ -7,15 +9,30 @@ export default defineVitestConfig({
7 */ 9 */
8 test: { 10 test: {
9 environment: 'nuxt', 11 environment: 'nuxt',
  12 + globals: true,
10 // you can optionally set Nuxt-specific environment options 13 // you can optionally set Nuxt-specific environment options
11 - // environmentOptions: { 14 + environmentOptions: {
12 - // nuxt: { 15 + nuxt: {
13 - // rootDir: fileURLToPath(new URL('./playground', import.meta.url)), 16 + rootDir: fileURLToPath(new URL('./', import.meta.url)),
14 - // domEnvironment: 'happy-dom', // 'happy-dom' (default) or 'jsdom' 17 + domEnvironment: 'happy-dom', // 'happy-dom' (default) or 'jsdom'
15 - // overrides: { 18 + overrides: {
16 - // // other Nuxt config you want to pass 19 + // other Nuxt config you want to pass
17 - // } 20 + },
18 - // } 21 + mock: {
19 - // } 22 + intersectionObserver: true,
  23 + indexedDb: true,
  24 + }
  25 + },
  26 + },
  27 + coverage: {
  28 + provider: 'v8',
  29 + reporter: ['text', 'json', 'html'],
  30 + }
  31 + },
  32 + resolve: {
  33 + alias: {
  34 + '~': fileURLToPath(new URL('./', import.meta.url)),
  35 + '@': fileURLToPath(new URL('./', import.meta.url)),
  36 + }
20 } 37 }
21 }) 38 })