mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-08 16:22:16 +01:00
119 lines
4.0 KiB
TypeScript
119 lines
4.0 KiB
TypeScript
/* 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]);
|
|
});
|
|
|
|
it("keeps a project's tasks visible when the search term matches the project name", async () => {
|
|
const project = {
|
|
id: 'p-dummy',
|
|
name: 'dummy',
|
|
color: '#fff',
|
|
client_id: null,
|
|
is_archived: false,
|
|
} as unknown as Project;
|
|
const tasks = [
|
|
{ id: 't-1', name: 'design', project_id: 'p-dummy', is_done: false },
|
|
{ id: 't-2', name: 'build', project_id: 'p-dummy', is_done: false },
|
|
] as unknown as Task[];
|
|
|
|
const wrapper = mountDropdown({ projects: [project], tasks });
|
|
await nextTick();
|
|
await nextTick();
|
|
|
|
const searchInput = wrapper.find('[data-testid="client_dropdown_search"]');
|
|
await searchInput.setValue('dummy');
|
|
await nextTick();
|
|
|
|
// project itself shows up
|
|
expect(wrapper.find('[data-project-id="p-dummy"]').exists()).toBe(true);
|
|
// and its tasks are still available even though they don't match "dummy":
|
|
// the task expander keeps showing all of the project's tasks
|
|
expect(wrapper.text()).toContain('2 Tasks');
|
|
});
|
|
});
|