Vuetify 3 TypeScript Tutorial Series -Part 11

Habibi Coding | حبيبي كودنق
Nerd For Tech
Published in
6 min readFeb 25, 2024
tutorial banner

In Part 10 of this tutorial series, we covered the following steps:

  • Wrote tests for taskApi.ts
  • Wrote tests for editTask.ts
  • Wrote tests for generateTask.ts

If you missed Part 10, you can find it here: Part 10

Test fetch tasks

Okay let us start to write a test for the composable file getTasks.ts so create in tests -> impl the file getTasks.test.ts and add these lines:

import {describe, expect, it, vi} from 'vitest';
import {taskService} from "../../src/services/taskApi";
import {getTasks} from "../../src/composables/getTasks";
import {AxiosError} from "axios";
import {mockTaskFetchResponse} from "../helper/mockResponse";


describe('getTasks tests', () => {
it('fetchTasks fills tasks array with mockTasksResponse', async () => {
// Mock taskService.getTasks to return mockTasksResponse
taskService.getTasks = async () => ({data: mockTaskFetchResponse});

// Call fetchTasks
const {fetchTasks, tasks, isLoading, isNetworkError} = getTasks();
await fetchTasks('');

// Assert tasks array contains mockTasksResponse
expect(tasks).toEqual(mockTaskFetchResponse);
expect(isLoading.value).toBe(false);
expect(isNetworkError.value).toBe(false);
});

it('handles error in fetchTasks', async () => {
const errorMessage = 'Network error';
const mockError = new AxiosError(errorMessage);

// Replace getTasks with a function that rejects with the mock error
taskService.getTasks = vi.fn(() => Promise.reject(mockError));

// Use the composable function
const {fetchTasks, isLoading, isNetworkError, axiosError} = getTasks();

// Call fetchTasks and expect it to handle the error
await fetchTasks('');

// Check the states of isLoading, isNetworkError, and axiosError
expect(isLoading.value).toBe(false);
expect(isNetworkError.value).toBe(true);
expect(axiosError.value).toEqual(mockError);
expect(mockError.message).toEqual(errorMessage);
});
});

Testing Successful Task Fetching:

  • The first test checks if fetchTasks successfully fills the tasks array with mockTaskFetchResponse.
  • It mocks taskService.getTasks to return a resolved promise with mockTaskFetchResponse.
  • Upon calling fetchTasks, it verifies that the tasks array matches the mock response, isLoading is false (indicating loading has completed), and isNetworkError is false (no network error occurred).

Testing Error Handling in Task Fetching:

  • The second test simulates an error during task fetching.
  • It mocks taskService.getTasks to return a rejected promise with mockError (an AxiosError instance containing a network error message).
  • After calling fetchTasks, it checks the application's state:
  • isLoading should be false (loading process ended).
  • isNetworkError should be true (an error occurred).

Another way to run all tests is to enter in Terminal:

pnpm test:unit
pnpm test:unit

You should see this output:

all tests passed

Test remove task

Next, we should write a test for the composable file removeTask.ts so create in tests -> impl the file removeTask.test.ts and add these lines:

import {describe, expect, it, vi} from 'vitest';
import {taskService} from "../../src/services/taskApi";
import {removeTask} from "../../src/composables/removeTask";
import {Ref, ref} from "vue";
import {AxiosError} from "axios";


describe('removeTask tests', () => {
const id = 1;
const isLoading: Ref<boolean> = ref(false);
const isNetworkError: Ref<boolean> = ref(false);
const axiosError: Ref<AxiosError | unknown> = ref(null);
const fetchTasks = vi.fn();
const taskType = 'completed';

it('when delete task is called then expect success path', async () => {
taskService.deleteTask = async () => ({});

await removeTask(id, isLoading, isNetworkError, axiosError, fetchTasks, taskType);

expect(isLoading.value).toBe(false);
expect(isNetworkError.value).toBe(false);
expect(fetchTasks).toHaveBeenCalled();
});

it('when delete task is called then expect network error', async () => {
const errorMessage = 'Network error';
const mockError = new AxiosError(errorMessage);
taskService.deleteTask = vi.fn(() => Promise.reject(mockError));

await removeTask(id, isLoading, isNetworkError, axiosError, fetchTasks, taskType);

expect(isLoading.value).toBe(false);
expect(isNetworkError.value).toBe(true);
expect(axiosError.value).toEqual(mockError);
expect(mockError.message).toEqual(errorMessage);
});
});

Testing Successful Task Deletion:

  • The first test, labeled “when delete task is called then expect success path,” simulates a successful task deletion.
  • It mocks taskService.deleteTask to return an empty response, indicating success.
  • After calling removeTask, it checks:
  • isLoading is false, showing the loading state has ended.
  • isNetworkError is false, indicating no error occurred.

Testing Error Handling in Task Deletion:

  • The second test, “when delete task is called then expect network error,” simulates an error scenario during task deletion.
  • It mocks taskService.deleteTask to return a rejected promise with mockError (an AxiosError instance).

After calling removeTask, it verifies:

  • isLoading is false, confirming the end of the loading state.
  • isNetworkError is true, indicating an error was encountered.
  • axiosError matches mockError, ensuring the error is correctly captured and stored.
  • The error message in mockError is as expected (errorMessage).

Now, let us see the test coverage for our tests, enter in the Terminal:

pnpm test:coverage
pnpm test:coverage

You should see this in the Terminal:

test coverage

There is also a folder coverage in your project with a file index.html :

coverage -> index.html

Command shortcuts

Okay, after we have now finished the testing part for our app. Let us take a look at our command shortcuts in package.json :

package.json

Summary of the shortcut commands:

  1. "dev": "vite": Starts the Vite development server. Running pnpm dev will start your project in development mode, usually with hot module replacement (HMR), making it easy to develop and test changes in real-time.
  2. "build": "vite build": Compiles and bundles your application for production deployment. Running pnpm build will generate the production files, typically in a dist directory.
  3. "preview": "vite preview": Serves the production build of your app for testing. This command lets you locally preview the production build after running the build script.
  4. "lint": "eslint . --fix --ignore-path .gitignore": Runs ESLint to analyze your code for potential errors or code style issues and automatically fixes them where possible. The --ignore-path .gitignore option ensures it skips files ignored by Git.
  5. "test:unit": "vitest": Runs unit tests using Vitest, a test runner. This script is used to execute the test cases written in your project.
  6. "test:coverage": "vitest --coverage": Generates a test coverage report using Vitest. This will tell you how much of your codebase is covered by tests, which is useful for maintaining your project.

The keys in the scripts section of your package.json from your Vuetify project define various command shortcuts that can be run from the terminal to perform common tasks in the development process.

Since we are already familiar with pnpm dev & pnpm test:unit & pnpm test:coverage . Let us run now pnpm build in the Terminal:

pnpm build

You should see the following outcome:

build success

As you can see the build was successful and you now have a dist folder:

dist folder

You must test always if pnpm build is successful because if it fails there is something wrong with your project and you need to fix it. Because no successful build also means you can’t create a Docker image out of your project (What we will do in the next part).

The next shortcut command to test is pnpm preview :

pnpm preview

By clicking on http://localhost:4173/ you should see the production build:

production build

Lastly let us see if our ES Linter works by entering pnpm lint in the Terminal:

pnpm lint

Congrats / Mabrook / مبروك no linter warnings in the project.

With that, we conclude the first part of this tutorial series. If you found it useful and informative, give it a clap. Here is the final and last part, Part 12

Don’t forget to check out the video playlist on YouTube.

Here is the source code on GitHub, check out the branch: part-eleven

--

--