Skip to content

Commit

Permalink
Features/219 frontend tests (#223)
Browse files Browse the repository at this point in the history
* wip

* Fix login not failing properly.

* Add an initial user-store test along with test config files.

* ➕ add alert store

* 📦 update frontend dependencies

* ➕ add appointment store tests

* ➕ add calendar store tests

---------

Co-authored-by: Andreas Müller <mail@devmount.de>
  • Loading branch information
MelissaAutumn and devmount authored Jan 12, 2024
1 parent fea6eec commit c687fde
Show file tree
Hide file tree
Showing 11 changed files with 10,216 additions and 641 deletions.
8,174 changes: 8,174 additions & 0 deletions frontend/package-lock.json

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --ignore-path .gitignore",
"lint:fix": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
"lint:fix": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
"test": "vitest"
},
"dependencies": {
"@auth0/auth0-vue": "^2.0.2",
Expand All @@ -17,7 +18,7 @@
"@tailwindcss/forms": "^0.5.3",
"@vitejs/plugin-vue": "^4.5.2",
"@vueuse/components": "^10.4.1",
"@vueuse/core": "^10.4.1",
"@vueuse/core": "^10.7.0",
"core-js": "^3.8.3",
"dayjs": "^1.11.5",
"pinia": "^2.1.6",
Expand All @@ -37,7 +38,11 @@
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-vue": "^9.19.2",
"jsdom": "^23.0.1",
"msw": "^2.0.11",
"node-fetch": "^3.3.2",
"postcss": "^8.4.17",
"vite-plugin-eslint": "^1.8.1"
"vite-plugin-eslint": "^1.8.1",
"vitest": "^1.1.0"
}
}
5 changes: 5 additions & 0 deletions frontend/src/stores/appointment-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import { defineStore } from 'pinia';
import { appointmentState } from '@/definitions';
import { useUserStore } from '@/stores/user-store';
import dj from 'dayjs';
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";

dj.extend(utc);
dj.extend(timezone);

const initialData = {
appointments: [],
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/stores/user-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const useUserStore = defineStore('user', {
formData.set('password', password);
const {error, data} = await fetch('token').post(formData).json();

if (!data.value.access_token) {
if (error.value || !data.value.access_token) {
return false;
}

Expand Down
5 changes: 5 additions & 0 deletions frontend/test/setup/fixFetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Polyfill to fix use/createFetch...
// Reference: https://github.com/reduxjs/redux-toolkit/issues/3254#issuecomment-1587624955
import nodeFetch, { Request, Response } from 'node-fetch';

Object.assign(global, { fetch: nodeFetch, Request, Response });
45 changes: 45 additions & 0 deletions frontend/test/stores/alert-store.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { expect, test, beforeEach, describe } from 'vitest';
import { useSiteNotificationStore } from '@/stores/alert-store';
import { createPinia, setActivePinia } from 'pinia';


describe('Site Notifications Store', () => {
// Create a pinia instance before each test
beforeEach(() => {
setActivePinia(createPinia());
});

test('visible', () => {
const alert = useSiteNotificationStore();
alert.show(123, 'title', 'message', 'url');
expect(alert.isVisible === true);
});
test('attributes', () => {
const alert = useSiteNotificationStore();
alert.show(123, 'title', 'message', 'url');
expect(alert.title === 'title');
expect(alert.actionUrl === 'url');
expect(alert.message === 'message');
});
test('same', () => {
const alert = useSiteNotificationStore();
alert.show(123, 'title', 'message', 'url');
expect(alert.isSameNotification(123) === true);
});
test('invisible at start', () => {
const alert = useSiteNotificationStore();
expect(alert.isVisible === false);
});
test('invisible after reset', () => {
const alert = useSiteNotificationStore();
alert.show(123, 'title', 'message', 'url');
alert.reset();
expect(alert.isVisible === false);
});
test('lock', () => {
const alert = useSiteNotificationStore();
alert.lock(123);
expect(alert.isVisible === false);
expect(alert.isSameNotification(123) === true);
});
});
101 changes: 101 additions & 0 deletions frontend/test/stores/appointment-store.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {
expect,
test,
beforeEach,
describe,
beforeAll,
afterAll,
afterEach,
} from 'vitest';
import { useAppointmentStore } from '@/stores/appointment-store';
import { createPinia, setActivePinia } from 'pinia';
import { setupServer } from 'msw/node';
import { HttpResponse, http } from 'msw';
import { createFetch } from '@vueuse/core';

const API_URL = 'http://localhost';

const restHandlers = [
http.get(`${API_URL}/me/appointments`, async (request) => {
return HttpResponse.json([
{
calendar_id: 1,
title: "title",
duration: 180,
location_type: 2,
slots: [
{ start: "3000-01-01T09:00:00Z", duration: 60 },
{ start: "3000-01-01T11:00:00Z", duration: 15 },
{ start: "3000-01-01T15:00:00Z", duration: 275 },
],
},
{
calendar_id: 1,
title: "title",
duration: 180,
location_type: 2,
slots: [
{ start: "2024-01-01T09:00:00Z", duration: 60 },
{ start: "2024-01-01T11:00:00Z", duration: 15, attendee_id: 1 },
{ start: "2024-01-01T15:00:00Z", duration: 275 },
],
},
]);
}),
];

const server = setupServer(...restHandlers);
server.events.on('request:start', ({ request }) => {
// console.log('Outgoing:', request.method, request.url);
});

describe('Appointment Store', () => {
// Create a pinia instance before each test
beforeEach(() => {
setActivePinia(createPinia());
});
// Start server before all tests
beforeAll(() => server.listen());

// Close server after all tests
afterAll(() => server.close());

// Reset handlers after each test `important for test isolation`
afterEach(() => server.resetHandlers());


test('init', () => {
const apmt = useAppointmentStore();
expect(apmt.isLoaded === false);
expect(apmt.appointments.length === 0);
});

test('fetch', async () => {
const apmt = useAppointmentStore();
await apmt.fetch(createFetch({ baseUrl: API_URL }));
expect(apmt.appointments.length === 2);
expect(apmt.appointments[0].slots.length === 3);
});

test('pending', async () => {
const apmt = useAppointmentStore();
await apmt.fetch(createFetch({ baseUrl: API_URL }));
expect(apmt.pendingAppointments.length === 1);
});

test('reset', async () => {
const apmt = useAppointmentStore();
await apmt.fetch(createFetch({ baseUrl: API_URL }));

// Check if appointments exist
expect(apmt.isLoaded === true);
expect(apmt.appointments.length === 2);

// Reset the user which should null all user data.
apmt.reset();

// Ensure our data is null/don't exist
expect(apmt.isLoaded === false);
expect(apmt.appointments.length === 0);
});
});
96 changes: 96 additions & 0 deletions frontend/test/stores/calendar-store.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {
expect,
test,
beforeEach,
describe,
beforeAll,
afterAll,
afterEach,
} from 'vitest';
import { useCalendarStore } from '@/stores/calendar-store';
import { createPinia, setActivePinia } from 'pinia';
import { setupServer } from 'msw/node';
import { HttpResponse, http } from 'msw';
import { createFetch } from '@vueuse/core';

const API_URL = 'http://localhost';

const restHandlers = [
http.get(`${API_URL}/me/calendars`, async (request) => {
return HttpResponse.json([
{
id: 1,
title: "title",
color: "#123456",
connected: true,
},
{
id: 2,
title: "title",
color: "#123456",
connected: false,
},
]);
}),
];

const server = setupServer(...restHandlers);
server.events.on('request:start', ({ request }) => {
// console.log('Outgoing:', request.method, request.url);
});

describe('Calendar Store', () => {
// Create a pinia instance before each test
beforeEach(() => {
setActivePinia(createPinia());
});
// Start server before all tests
beforeAll(() => server.listen());

// Close server after all tests
afterAll(() => server.close());

// Reset handlers after each test `important for test isolation`
afterEach(() => server.resetHandlers());


test('init', () => {
const calStore = useCalendarStore();
expect(calStore.isLoaded === false);
expect(calStore.allCalendars.length === 0);
});

test('fetch', async () => {
const calStore = useCalendarStore();
await calStore.fetch(createFetch({ baseUrl: API_URL }));
expect(calStore.allCalendars.length === 2);
});

test('unconnected', async () => {
const calStore = useCalendarStore();
await calStore.fetch(createFetch({ baseUrl: API_URL }));
expect(calStore.unconnectedCalendars.length === 1);
});

test('connected', async () => {
const calStore = useCalendarStore();
await calStore.fetch(createFetch({ baseUrl: API_URL }));
expect(calStore.connectedCalendars.length === 1);
});

test('reset', async () => {
const calStore = useCalendarStore();
await calStore.fetch(createFetch({ baseUrl: API_URL }));

// Check if calendars exist
expect(calStore.isLoaded === true);
expect(calStore.allCalendars.length === 2);

// Reset the user which should null all user data.
calStore.reset();

// Ensure our data is null/don't exist
expect(calStore.isLoaded === false);
expect(calStore.allCalendars.length === 0);
});
});
Loading

0 comments on commit c687fde

Please sign in to comment.