mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-14 11:12:16 +01:00
add timesheet unit and e2e tests; add unit test CI setup
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
/* eslint-disable vue/one-component-per-file */
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { defineComponent, h, nextTick, onMounted } from 'vue';
|
||||
import TimeTrackerProjectTaskDropdown from './TimeTrackerProjectTaskDropdown.vue';
|
||||
import type { Client, Project, Task } from '@/packages/api/src';
|
||||
|
||||
const DropdownStub = defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(_, { emit, slots }) {
|
||||
onMounted(() => emit('update:modelValue', true));
|
||||
return () => h('div', [slots.trigger?.(), slots.content?.()]);
|
||||
},
|
||||
});
|
||||
|
||||
const FocusTrapStub = defineComponent({
|
||||
setup(_, { slots }) {
|
||||
return () => h('div', slots.default?.());
|
||||
},
|
||||
});
|
||||
|
||||
function mountDropdown(props: Record<string, unknown> = {}) {
|
||||
return mount(TimeTrackerProjectTaskDropdown, {
|
||||
props: {
|
||||
project: null,
|
||||
task: null,
|
||||
projects: [] as Project[],
|
||||
tasks: [] as Task[],
|
||||
clients: [] as Client[],
|
||||
createProject: vi.fn(),
|
||||
createClient: vi.fn(),
|
||||
currency: 'EUR',
|
||||
enableEstimatedTime: false,
|
||||
organizationBillableRate: null,
|
||||
canCreateProject: false,
|
||||
...props,
|
||||
},
|
||||
global: {
|
||||
stubs: {
|
||||
Dropdown: DropdownStub,
|
||||
UseFocusTrap: FocusTrapStub,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function openDropdown() {
|
||||
const wrapper = mountDropdown();
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
describe('TimeTrackerProjectTaskDropdown', () => {
|
||||
it('keeps the existing empty-string no-project value by default', async () => {
|
||||
const wrapper = await openDropdown();
|
||||
|
||||
await wrapper.find('[data-project-id=""]').trigger('click');
|
||||
|
||||
expect(wrapper.emitted('update:project')?.at(-1)).toEqual(['']);
|
||||
expect(wrapper.emitted('changed')?.at(-1)).toEqual(['', null]);
|
||||
});
|
||||
|
||||
it('can emit null for no-project consumers that use null as the domain value', async () => {
|
||||
const wrapper = mountDropdown({ project: 'p-1', noProjectValue: null });
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
|
||||
await wrapper.find('[data-project-id=""]').trigger('click');
|
||||
|
||||
expect(wrapper.emitted('update:project')?.at(-1)).toEqual([null]);
|
||||
expect(wrapper.emitted('changed')?.at(-1)).toEqual([null, null]);
|
||||
});
|
||||
|
||||
it('still exposes "No Project" when projects are empty and project creation is allowed', async () => {
|
||||
const wrapper = mountDropdown({ canCreateProject: true });
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
|
||||
await wrapper.find('[data-project-id=""]').trigger('click');
|
||||
|
||||
expect(wrapper.emitted('changed')?.at(-1)).toEqual(['', null]);
|
||||
});
|
||||
});
|
||||
44
resources/js/packages/ui/src/utils/time.test.ts
Normal file
44
resources/js/packages/ui/src/utils/time.test.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { formatHumanReadableDuration, formatReportingDuration } from './time';
|
||||
|
||||
const seconds = 14 * 3600 + 45 * 60 + 6; // 14h 45m 06s
|
||||
|
||||
describe('formatHumanReadableDuration', () => {
|
||||
test('decimal', () => {
|
||||
expect(formatHumanReadableDuration(seconds, 'decimal', 'comma-point')).toBe('14.75 h');
|
||||
});
|
||||
|
||||
test('hours-minutes', () => {
|
||||
expect(formatHumanReadableDuration(seconds, 'hours-minutes')).toBe('14h 45min');
|
||||
});
|
||||
|
||||
test('hours-minutes-colon-separated', () => {
|
||||
expect(formatHumanReadableDuration(seconds, 'hours-minutes-colon-separated')).toBe('14:45');
|
||||
});
|
||||
|
||||
test('hours-minutes-seconds-colon-separated', () => {
|
||||
expect(formatHumanReadableDuration(seconds, 'hours-minutes-seconds-colon-separated')).toBe(
|
||||
'14:45:06'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatReportingDuration', () => {
|
||||
test('decimal', () => {
|
||||
expect(formatReportingDuration(seconds, 'decimal', 'comma-point')).toBe('14.75 h');
|
||||
});
|
||||
|
||||
test('hours-minutes', () => {
|
||||
expect(formatReportingDuration(seconds, 'hours-minutes')).toBe('14:45:06');
|
||||
});
|
||||
|
||||
test('hours-minutes-colon-separated', () => {
|
||||
expect(formatReportingDuration(seconds, 'hours-minutes-colon-separated')).toBe('14:45:06');
|
||||
});
|
||||
|
||||
test('hours-minutes-seconds-colon-separated', () => {
|
||||
expect(formatReportingDuration(seconds, 'hours-minutes-seconds-colon-separated')).toBe(
|
||||
'14:45:06'
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user