Toggle navigation
Toggle navigation
This project
Loading...
Sign in
Bruno Predot
/
tmdb_test
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
Bruno Predot
2025-05-17 11:40:51 +0200
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
185d531abd4be6a8e2e0ab1d50fb0331488bdf16
185d531a
1 parent
8d8634a3
lintfix selon les règles de @antfu/eslint-config.
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
199 additions
and
174 deletions
components/MovieCard.vue
components/MovieCommentList.vue
components/MoviesList.vue
components/details/MovieGender.vue
components/details/ScoreAndVote.vue
components/form/MovieCommentForm.vue
components/test/HelloWorld.spec.ts
components/ui-components/BackdropImage.vue
components/ui-components/Loader.vue
components/ui-components/Poster.vue
components/ui-components/SearchBar.vue
components/ui-components/SkeletonMoviesLoader.vue
components/ui-components/TinyMceFieldEditor.vue
composables/tMDB.ts
interfaces/credit.ts
interfaces/movie.ts
pages/movies/[id]/index.vue
test/components/MovieGender.spec.ts
test/components/ScoreAndVote.spec.ts
type/commentForm.ts
vitest.config.m.ts
components/MovieCard.vue
View file @
185d531
<script lang="ts" setup>
//#region --Props--.
import type { MovieInterface } from "~/interfaces/movie";
// #region --Props--.
import { useDateFormat } from "@vueuse/core";
import { FilmIcon } from "lucide-vue-next";
import type { MovieInterface } from "~/interfaces/movie";
//#endregion
// #endregion
//#region --Props--.
//
#region --Props--.
/** Typescript typage */
defineProps<{
movie: MovieInterface;
...
...
@@ -18,7 +18,7 @@ defineProps<{
// nullable: false,
// },
// });
//#endregion
//
#endregion
</script>
<template>
...
...
components/MovieCommentList.vue
View file @
185d531
<script lang="ts" setup>
//#region --Import--.
//
#region --Import--.
import type { MovieCommentInterface } from "~/interfaces/movieComment";
import { MessageSquareIcon } from "lucide-vue-next";
//#endregion
//
#endregion
//#region --Props--.
//
#region --Props--.
/** Typescript typage */
const props = defineProps<{
comments: Array<MovieCommentInterface>;
...
...
@@ -17,9 +17,9 @@ const props = defineProps<{
// nullable: false,
// },
// });
//#endregion
//
#endregion
//#region --Watch--.
//
#region --Watch--.
watch(
() => props.comments,
(comments) => {
...
...
@@ -34,7 +34,7 @@ watch(
},
{ immediate: true },
);
//#endregion
//
#endregion
</script>
<template>
...
...
components/MoviesList.vue
View file @
185d531
<script lang="ts" setup>
//#region --import--.
import type { MovieInterface } from "~/interfaces/movie";
import { SearchXIcon } from "lucide-vue-next";
// #region --import--.
import { onBeforeUnmount, ref } from "vue";
import { useTMDB } from "~/composables/tMDB";
import { Movie } from "~/models/movie";
import { SearchXIcon } from "lucide-vue-next";
import type { MovieInterface } from "~/interfaces/movie";
//#endregion
// #endregion
//#region --Declaration--.
//
#region --Declaration--.
const { fetchPopularMovies, searchMovies } = useTMDB();
//#endregion
//
#endregion
//#region --Data/refs--.
//
#region --Data/refs--.
const isInitialLoading = ref(true);
const isLoadingMore = ref(false);
const currentPage = ref(1);
...
...
@@ -21,20 +21,20 @@ const searchQuery = ref("");
const loadMoreTrigger = ref<HTMLElement | null>(null);
/** Instance de IntersectionObserver */
const observer = ref<IntersectionObserver | null>(null);
//#endregion
//
#endregion
//#region --Computed--.
//
#region --Computed--.
const movies = computed(() => {
return useRepo(Movie).query().orderBy("popularity", "desc").get() as unknown as MovieInterface[];
});
//#endregion
//
#endregion
//#region --Function--.
//
#region --Function--.
/**
* Fetch popular movies
* @param page
*/
const fetchMovies = async (page: number) =>
{
async function fetchMovies(page: number)
{
try {
isLoadingMore.value = true;
const data = await fetchPopularMovies(page);
...
...
@@ -42,26 +42,29 @@ const fetchMovies = async (page: number) => {
if (isInitialLoading.value) {
// First fetch, erase old data before save.
useRepo(Movie).fresh(data.results);
} else {
}
else {
// Add to store collection.
useRepo(Movie).save(data.results);
}
totalPages.value = data.total_pages;
currentPage.value = page;
} catch (error) {
}
catch (error) {
console.error("Error fetching popular movies:", error);
} finally {
}
finally {
isInitialLoading.value = false;
isLoadingMore.value = false;
}
}
;
}
/**
* Search movies
* @param query
* @param page
*/
const search = async (query: string, page: number) =>
{
async function search(query: string, page: number)
{
// If empty search, fetch popular movies.
if (!query.trim()) {
await fetchMovies(1);
...
...
@@ -77,19 +80,22 @@ const search = async (query: string, page: number) => {
if (isInitialLoading.value) {
// First fetch, erase old data before save.
useRepo(Movie).fresh(data.results);
} else {
}
else {
// Add to store collection.
useRepo(Movie).save(data.results);
}
totalPages.value = data.total_pages;
currentPage.value = page;
} catch (error) {
}
catch (error) {
console.error("Error searching movies:", error);
} finally {
}
finally {
isInitialLoading.value = false;
isLoadingMore.value = false;
}
}
;
}
function createIntersectionObserver() {
return new IntersectionObserver(
...
...
@@ -99,7 +105,8 @@ function createIntersectionObserver() {
if (searchQuery.value) {
// Continue searching query if already active.
search(searchQuery.value, currentPage.value + 1);
} else {
}
else {
// Continue fetching popular movies.
fetchMovies(currentPage.value + 1);
}
...
...
@@ -122,9 +129,9 @@ function handleClearSearchEvent() {
fetchMovies(1);
}
//#endregion
//
#endregion
//#region --Global event--.
//
#region --Global event--.
onMounted(() => {
// First loading.
fetchMovies(1);
...
...
@@ -146,7 +153,7 @@ onBeforeUnmount(() => {
observer.value.disconnect();
}
});
//#endregion
//
#endregion
</script>
<template>
...
...
@@ -157,8 +164,8 @@ onBeforeUnmount(() => {
<!-- Barre de recherche -->
<ui-components-search-bar
placeholder="Rechercher un film..."
@event
:
search="handleSearchEvent"
@event
:clear_
search="handleClearSearchEvent"
@event
-
search="handleSearchEvent"
@event
-clear-
search="handleClearSearchEvent"
/>
<!-- Loading Skeleton -->
...
...
components/details/MovieGender.vue
View file @
185d531
<script lang="ts" setup>
//#region --Import--.
//
#region --Import--.
import type { Genre } from "~/interfaces/movie";
//#endregion
//
#endregion
//#region --Props--.
//
#region --Props--.
defineProps({
genres: {
type: Array<Genre>,
...
...
@@ -11,7 +11,7 @@ defineProps({
nullable: false,
},
});
//#endregion
//
#endregion
</script>
<template>
...
...
components/details/ScoreAndVote.vue
View file @
185d531
<script lang="ts" setup>
//#region --Props--.
//
#region --Props--.
/** Typescript typage */
defineProps<{
score: number;
...
...
@@ -18,20 +18,20 @@ defineProps<{
// nullable: false,
// },
// });
//#endregion
//
#endregion
//#region --Function--.
//
#region --Function--.
/**
* Format vote count if > 1000.
* @param count
*/
const formatVoteCount = (count: number) =>
{
function formatVoteCount(count: number)
{
if (count >= 1000) {
return `${(count / 1000).toFixed(1)}k votes`;
}
return `${count} votes`;
}
;
//#endregion
}
//
#endregion
</script>
<template>
...
...
components/form/MovieCommentForm.vue
View file @
185d531
<script lang="ts" setup>
//#region --Import--.
import type { Comment } from "~/type/commentForm";
// #region --Import--.
import { useVuelidate } from "@vuelidate/core";
import { helpers, maxLength, maxValue, minLength, minValue, required } from "@vuelidate/validators";
import type { Comment } from "~/type/commentForm";
//#endregion
// #endregion
//#region --Emit--.
const emit = defineEmits(["event:submit"]);
//#endregion
//#region --Props--.
// #region --Props--.
defineProps({
isSubmitting: {
type: Boolean,
...
...
@@ -18,9 +14,13 @@ defineProps({
default: false,
},
});
//#endregion
// #endregion
// #region --Emit--.
const emit = defineEmits(["eventSubmit"]);
// #endregion
//#region --Data/ref--.
//
#region --Data/ref--.
const initialState: Comment = {
username: "",
message: "",
...
...
@@ -35,6 +35,7 @@ const rules = {
maxLength: helpers.withMessage("Le nom d'utilisateur ne peut pas dépasser 50 caractères", maxLength(50)),
alpha: helpers.withMessage(
"Le nom d'utilisateur ne peut contenir que des lettres",
// eslint-disable-next-line regexp/no-obscure-range
helpers.regex(/^[a-zA-ZÀ-ÿ\s]+$/),
),
},
...
...
@@ -54,15 +55,15 @@ const formData = reactive({
...initialState,
});
const v$ = useVuelidate(rules, formData);
//#endregion
//
#endregion
// const errormessages = computed(() => {
// return v$.value.message.$errors.map((e) => e.$message);
// });
//#region --Function--.
//
#region --Function--.
async function submitComment() {
emit("event
:s
ubmit", formData);
emit("event
S
ubmit", formData);
}
function clear() {
...
...
@@ -80,7 +81,7 @@ function handleMessageEvent(event: string) {
// console.log(formData.message.replace(/(<([^>]+)>)/ig, ''));
}
//#endregion
//
#endregion
</script>
<template>
...
...
@@ -105,7 +106,7 @@ function handleMessageEvent(event: string) {
@blur="v$.rating.$touch"
@input="v$.rating.$touch"
/>
<!-- <pre>{{ errormessages }}</pre>-->
<!-- <pre>{{ errormessages }}</pre>
-->
<ui-components-tiny-mce-field-editor
:error-message="v$?.message?.$errors[0]?.$message ? (v$.message.$errors[0].$message as string) : ''"
:model-value="formData.message"
...
...
components/test/HelloWorld.spec.ts
View file @
185d531
import
{
describe
,
expect
,
it
}
from
"vitest"
;
import
{
mount
}
from
"@vue/test-utils"
;
import
{
describe
,
expect
,
it
}
from
"vitest"
;
import
HelloWorld
from
"./HelloWorld.vue"
;
describe
(
"
H
elloWorld"
,
()
=>
{
describe
(
"
h
elloWorld"
,
()
=>
{
it
(
"component renders Hello world properly"
,
()
=>
{
const
wrapper
=
mount
(
HelloWorld
);
expect
(
wrapper
.
text
()).
toContain
(
"Hello world"
);
...
...
components/ui-components/BackdropImage.vue
View file @
185d531
<script lang="ts" setup>
//#region --Props--.
//
#region --Props--.
defineProps({
src: {
type: String,
...
...
@@ -12,11 +12,11 @@ defineProps({
nullable: false,
},
});
//#endregion
//
#endregion
//#region --Declaration--.
//
#region --Declaration--.
const w: Window = window;
//#endregion
//
#endregion
</script>
<template>
...
...
components/ui-components/Loader.vue
View file @
185d531
<script lang="ts" setup>
//#region --Props--.
//
#region --Props--.
defineProps({
isLoading: {
type: Boolean,
...
...
@@ -13,7 +13,7 @@ defineProps({
default: false,
},
});
//#endregion
//
#endregion
</script>
<template>
...
...
components/ui-components/Poster.vue
View file @
185d531
<script setup lang="ts">
//#region --Props--.
//
#region --Props--.
import { FilmIcon } from "lucide-vue-next";
defineProps({
...
...
@@ -14,7 +14,7 @@ defineProps({
nullable: false,
},
});
//#endregion
//
#endregion
</script>
<template>
...
...
components/ui-components/SearchBar.vue
View file @
185d531
<script lang="ts" setup>
//#region --import--.
import { useDebounceFn } from "@vueuse/core";
// #region --import--.
import { SearchIcon, XIcon } from "lucide-vue-next";
import { ref } from "vue";
import { useDebounceFn } from "@vueuse/core";
//#endregion
// #endregion
//#region --Emits--.
const emit = defineEmits(["event:search", "event:clear_search"]);
//#endregion
//#region --Props--.
// #region --Props--.
defineProps({
placeholder: {
type: String,
...
...
@@ -18,25 +14,29 @@ defineProps({
default: "",
},
});
//#endregion
// #endregion
// #region --Emits--.
const emit = defineEmits(["eventSearch", "eventClearSearch"]);
// #endregion
//#region --Data/refs--.
//
#region --Data/refs--.
const searchQuery = ref("");
//#endregion
//
#endregion
//#region --Function--.
//
#region --Function--.
/**
* Debounced function
*/
const handleSearchEvent = useDebounceFn(() => {
emit("event
:s
earch", searchQuery.value);
emit("event
S
earch", searchQuery.value);
}, 500);
function handleClearSearchEvent() {
searchQuery.value = "";
emit("event
:clear_s
earch");
emit("event
ClearS
earch");
}
//#endregion
//
#endregion
</script>
<template>
...
...
components/ui-components/SkeletonMoviesLoader.vue
View file @
185d531
<script lang="ts" setup>
//#region --Props--.
//
#region --Props--.
defineProps({
isInitialLoading: {
type: Boolean,
...
...
@@ -13,7 +13,7 @@ defineProps({
default: 12,
},
});
//#endregion
//
#endregion
</script>
<template>
...
...
components/ui-components/TinyMceFieldEditor.vue
View file @
185d531
<script lang="ts" setup>
//#region --Import--.
//
#region --Import--.
import Editor from "@tinymce/tinymce-vue";
import { ref, watch } from "vue";
//#endregion
//
#endregion
//#region --Declaration--.
const runtimeConfig = useRuntimeConfig();
//#endregion
// #region --Props--.
const props = defineProps<{
modelValue: string;
errorMessage: string;
}>();
// #endregion
//#region --Emit--.
//
#region --Emit--.
const emit = defineEmits<{
(e: "update:modelValue", value: string): void;
}>();
//#endregion
//
#endregion
//#region --Props--.
const props = defineProps<{
modelValue: string;
errorMessage: string;
}>();
//#endregion
// #region --Declaration--.
const runtimeConfig = useRuntimeConfig();
// #endregion
//#region --Data/ref--.
//
#region --Data/ref--.
const content = ref(props.modelValue);
const init = {
height: 300,
...
...
@@ -48,10 +48,10 @@ const init = {
"wordcount",
],
toolbar:
"undo redo | blocks | bold italic underline strikethrough |"
+
"bold italic forecolor | alignleft aligncenter " +
"alignright alignjustify | bullist numlist outdent indent | " +
"removeformat | help",
"undo redo | blocks | bold italic underline strikethrough |"
+ "bold italic forecolor | alignleft aligncenter "
+ "alignright alignjustify | bullist numlist outdent indent | "
+
"removeformat | help",
content_style: "body { font-family:Helvetica,Arial,sans-serif; font-size:14px }",
skin: "oxide-dark",
content_css: "dark",
...
...
@@ -59,9 +59,9 @@ const init = {
// valid_elements: [],
// entity_encoding : "raw",
};
//#endregion
//
#endregion
//#region --Watch--.
//
#region --Watch--.
watch(content, (newValue) => {
emit("update:modelValue", newValue);
});
...
...
@@ -74,12 +74,12 @@ watch(
}
},
);
//#endregion
//
#endregion
</script>
<template>
<div>
<
e
ditor
<
E
ditor
v-model="content"
:api-key="runtimeConfig.public.apiTinyMceSecret"
:init="init"
...
...
composables/tMDB.ts
View file @
185d531
import
type
{
RuntimeConfig
}
from
"nuxt/schema"
;
export
const
useTMDB
=
function
()
{
export
function
useTMDB
()
{
const
runtimeconfig
:
RuntimeConfig
=
useRuntimeConfig
();
const
apiUrl
=
runtimeconfig
.
public
.
apiTMDBUrl
;
const
apiKey
=
runtimeconfig
.
public
.
apiTMDBSecret
;
...
...
@@ -14,10 +14,12 @@ export const useTMDB = function () {
const
response
=
await
fetch
(
`
${
apiUrl
}
/movie/popular?api_key=
${
apiKey
}
&language=fr-FR&page=
${
page
}
`
);
if
(
!
response
.
ok
)
{
console
.
error
(
"An error occurred when fetching popular movies:"
);
}
else
{
}
else
{
return
await
response
.
json
();
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
"Error fetching popular movies:"
,
error
);
}
};
...
...
@@ -34,10 +36,12 @@ export const useTMDB = function () {
);
if
(
!
response
.
ok
)
{
console
.
error
(
"An error occurred when searching movies:"
);
}
else
{
}
else
{
return
await
response
.
json
();
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
"Error searching movies:"
,
error
);
}
};
...
...
@@ -51,10 +55,12 @@ export const useTMDB = function () {
const
response
=
await
fetch
(
`
${
apiUrl
}
/movie/
${
id
}
?api_key=
${
apiKey
}
&language=fr-FR`
);
if
(
!
response
.
ok
)
{
console
.
error
(
"An error occurred when fetching movie details:"
);
}
else
{
}
else
{
return
await
response
.
json
();
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
"Error fetching details:"
,
error
);
}
};
...
...
@@ -67,13 +73,15 @@ export const useTMDB = function () {
const
response
=
await
fetch
(
`
${
apiUrl
}
/movie/
${
id
}
/credits?api_key=
${
apiKey
}
&language=fr-FR`
);
if
(
!
response
.
ok
)
{
console
.
error
(
"An error occurred when fetching movie credits:"
);
}
else
{
}
else
{
return
await
response
.
json
();
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
"Error fetching movie credits:"
,
error
);
}
};
return
{
fetchPopularMovies
,
searchMovies
,
fetchMovieDetails
,
fetchMovieCredits
};
}
;
}
...
...
interfaces/credit.ts
View file @
185d531
...
...
@@ -7,10 +7,10 @@ export interface CreditInterface {
character
?:
string
;
}
export
type
CreditsResponse
=
{
export
interface
CreditsResponse
{
id
:
number
;
cast
:
CreditInterface
[];
crew
:
CreditInterface
[];
movie_id
:
unknown
;
movie
:
MovieInterface
;
}
;
}
...
...
interfaces/movie.ts
View file @
185d531
...
...
@@ -20,7 +20,7 @@ export interface MovieInterface {
credit
:
CreditsResponse
;
}
export
type
Genre
=
{
export
interface
Genre
{
id
:
number
;
name
:
string
;
}
;
}
...
...
pages/movies/[id]/index.vue
View file @
185d531
<script lang="ts" setup>
//#region --import--.
import type { WhereSecondaryClosure } from "pinia-orm";
import type { CreditsResponse } from "~/interfaces/credit";
import type { MovieInterface } from "~/interfaces/movie";
import type { MovieCommentInterface } from "~/interfaces/movieComment";
// #region --import--.
import { AlertTriangleIcon, ArrowLeftIcon } from "lucide-vue-next";
import { useTMDB } from "~/composables/tMDB";
import { computed, onMounted, ref } from "vue";
import { Movie } from "~/models/movie";
import type { MovieInterface } from "~/interfaces/movie";
import { useTMDB } from "~/composables/tMDB";
import { Credit } from "~/models/credit";
import type { CreditsResponse } from "~/interfaces/credit";
import type { MovieCommentInterface } from "~/interfaces/movieComment";
import { Movie } from "~/models/movie";
import { MovieComment } from "~/models/movieComment";
import type { WhereSecondaryClosure } from "pinia-orm";
//#endregion
// #endregion
//#region --Declaration--.
//
#region --Declaration--.
const { fetchMovieDetails, fetchMovieCredits } = useTMDB();
//#endregion
//
#endregion
//#region --Declaration--.
//
#region --Declaration--.
const { currentRoute } = useRouter();
//#endregion
//
#endregion
//#region --Data/ref--.
//
#region --Data/ref--.
const isLoading = ref(true);
const isSubmitting = ref(false);
//#endregion
//
#endregion
//#region --Computed--.
//
#region --Computed--.
const movieId = computed(() => {
if (currentRoute.value.params.id) {
if (typeof currentRoute.value.params.id === "string") {
if (typeof Number(+currentRoute.value.params.id) === "number") {
return +currentRoute.value.params.id as number;
} else {
}
else {
return currentRoute.value.params.id as string;
}
} else {
}
else {
return null;
}
} else {
}
else {
return null;
}
});
...
...
@@ -49,7 +52,8 @@ const movie = computed(() => {
.where("id", movieId.value as WhereSecondaryClosure<never> | null | undefined)
.withAll()
.first() as unknown as MovieInterface;
} else {
}
else {
return null;
}
});
...
...
@@ -59,8 +63,9 @@ const movie = computed(() => {
*/
const director = computed(() => {
if (unref(movie)?.credit?.crew) {
return movie.value?.credit.crew.find((person) => person.job === "Director");
} else {
return movie.value?.credit.crew.find(person => person.job === "Director");
}
else {
return null;
}
});
...
...
@@ -78,39 +83,42 @@ const comments = computed(() => {
.orderBy("createdAt", "desc")
.get();
});
//#endregion
//
#endregion
//#region --Function--.
//
#region --Function--.
/**
* Fetch movie details
*/
const fetchDetails = async (id: number | string) =>
{
async function fetchDetails(id: number | string)
{
try {
isLoading.value = true;
const data = await fetchMovieDetails(id);
// Add to store collection.
useRepo(Movie).save(data);
} catch (error) {
}
catch (error) {
console.error("Error fetching movie details:", error);
} finally {
}
finally {
isLoading.value = false;
}
}
;
}
/**
* Format runtime
* @param minutes
*/
const formatRuntime = (minutes: number) => {
if (!minutes) return "Durée inconnue";
function formatRuntime(minutes: number) {
if (!minutes)
return "Durée inconnue";
// Find nb hours.
const hours = Math.floor(minutes / 60);
// Find last minutes.
const mins = minutes % 60;
return `${hours}h ${mins}min`;
}
;
}
async function fetchCredits(id: number | string) {
try {
...
...
@@ -118,12 +126,13 @@ async function fetchCredits(id: number | string) {
data.movie_id = id;
// Add to store collection.
useRepo(Credit).save(data);
} catch (error) {
}
catch (error) {
console.error("Error fetching movie credits:", error);
}
}
//Ce n'est pas le film du siècle cependant, il est suffisamment intéressant pour passer une bonne soirée !
//
Ce n'est pas le film du siècle cependant, il est suffisamment intéressant pour passer une bonne soirée !
function handleSubmitEvent(event: MovieCommentInterface) {
isSubmitting.value = true;
event.movie_id = unref(movieId);
...
...
@@ -132,9 +141,9 @@ function handleSubmitEvent(event: MovieCommentInterface) {
isSubmitting.value = false;
}
//#endregion
//
#endregion
//#region --Global event--.
//
#region --Global event--.
onMounted(() => {
// Fetch data on component mount.
if (unref(movieId)) {
...
...
@@ -144,7 +153,7 @@ onMounted(() => {
}
// loadComments()
});
//#endregion
//
#endregion
</script>
<template>
...
...
@@ -245,7 +254,7 @@ onMounted(() => {
<h3 class="text-xl font-bold mt-8 mb-4">
Ajouter un commentaire
</h3>
<form-movie-comment-form @event
:
submit="handleSubmitEvent" />
<form-movie-comment-form @event
-
submit="handleSubmitEvent" />
<!-- Liste des commentaires -->
<movie-comment-list :comments="comments as unknown as MovieCommentInterface[]" />
...
...
test/components/MovieGender.spec.ts
View file @
185d531
//#region --Import--.
import
{
describe
,
expect
,
it
}
from
"vitest"
;
import
type
{
Genre
}
from
"~/interfaces/movie"
;
import
{
mount
}
from
"@vue/test-utils"
;
// #region --Import--.
import
{
describe
,
expect
,
it
}
from
"vitest"
;
import
MovieGender
from
"../../components/details/MovieGender.vue"
;
import
type
{
Genre
}
from
"~/interfaces/movie"
;
//#endregion
// #endregion
describe
(
"
M
ovieGender"
,
()
=>
{
describe
(
"
m
ovieGender"
,
()
=>
{
it
(
"affiche correctement les genres"
,
()
=>
{
// Données de test.
const
genres
:
Genre
[]
=
[
...
...
test/components/ScoreAndVote.spec.ts
View file @
185d531
//#region --Import--.
import
{
describe
,
expect
,
it
}
from
"vitest"
;
import
{
mount
}
from
"@vue/test-utils"
;
// #region --Import--.
import
{
describe
,
expect
,
it
}
from
"vitest"
;
import
ScoreAndVote
from
"../../components/details/ScoreAndVote.vue"
;
//#endregion
//
#endregion
describe
(
"
S
coreAndVote"
,
()
=>
{
describe
(
"
s
coreAndVote"
,
()
=>
{
it
(
"affiche correctement le score"
,
()
=>
{
// Monter le composant avec ses props.
const
wrapper
=
mount
(
ScoreAndVote
,
{
...
...
type/commentForm.ts
View file @
185d531
export
type
Comment
=
{
export
interface
Comment
{
username
:
string
;
message
:
string
;
rating
:
number
;
}
;
}
...
...
vitest.config.m.ts
View file @
185d531
import
{
defineVitestConfig
}
from
"@nuxt/test-utils/config"
;
import
{
fileURLToPath
}
from
"node:url"
;
import
{
defineVitestConfig
}
from
"@nuxt/test-utils/config"
;
export
default
defineVitestConfig
({
/**
...
...
Please
register
or
login
to post a comment