version-0.1 (#1)
Squash and merge of version-0.1 Co-authored-by: James Wampler <james@wamp.dev> Co-committed-by: James Wampler <james@wamp.dev>
This commit was merged in pull request #1.
This commit is contained in:
0
src/admin/tests/__mocks__/fileMock.js
Normal file → Executable file
0
src/admin/tests/__mocks__/fileMock.js
Normal file → Executable file
0
src/admin/tests/__mocks__/styleMock.js
Normal file → Executable file
0
src/admin/tests/__mocks__/styleMock.js
Normal file → Executable file
0
src/admin/tests/setup.ts
Normal file → Executable file
0
src/admin/tests/setup.ts
Normal file → Executable file
@@ -1,45 +0,0 @@
|
||||
import { describe, it, expect, beforeEach } from '@jest/globals';
|
||||
import { mount, VueWrapper } from '@vue/test-utils';
|
||||
import { createRouter, createMemoryHistory } from 'vue-router';
|
||||
import AppHeader from '@/components/AppHeader.vue';
|
||||
|
||||
function makeRouter() {
|
||||
return createRouter({
|
||||
history: createMemoryHistory(),
|
||||
routes: [
|
||||
{ path: '/', component: { template: '<div />' } },
|
||||
{ path: '/profile', component: { template: '<div />' } },
|
||||
{ path: '/settings', component: { template: '<div />' } },
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
describe('AppHeader', () => {
|
||||
let wrapper: VueWrapper;
|
||||
|
||||
beforeEach(async () => {
|
||||
const router = makeRouter();
|
||||
await router.push('/');
|
||||
|
||||
wrapper = mount(
|
||||
{ template: '<v-app><AppHeader /></v-app>', components: { AppHeader } },
|
||||
{ global: { plugins: [router] } },
|
||||
);
|
||||
});
|
||||
|
||||
it('renders the SVG microphone logo image', () => {
|
||||
const logo = wrapper.find('[data-testid="app-logo"]');
|
||||
expect(logo.exists()).toBe(true);
|
||||
expect(logo.attributes('alt')).toBe('MicCheck logo');
|
||||
});
|
||||
|
||||
it('renders the profile link', () => {
|
||||
const link = wrapper.find('[data-testid="profile-link"]');
|
||||
expect(link.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders the settings gear icon link', () => {
|
||||
const link = wrapper.find('[data-testid="settings-link"]');
|
||||
expect(link.exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
0
src/admin/tests/unit/api/client.spec.ts
Normal file → Executable file
0
src/admin/tests/unit/api/client.spec.ts
Normal file → Executable file
55
src/admin/tests/unit/components/EnvironmentTabBar.spec.ts
Normal file → Executable file
55
src/admin/tests/unit/components/EnvironmentTabBar.spec.ts
Normal file → Executable file
@@ -56,13 +56,6 @@ describe('EnvironmentTabBar', () => {
|
||||
expect(envApi.listEnvironments).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ThenNoProjectMessageIsShown', async () => {
|
||||
const wrapper = mountComponent();
|
||||
await flushPromises();
|
||||
|
||||
expect(wrapper.find('[data-testid="env-no-project-message"]').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('ThenCreateEnvironmentButtonIsDisabled', async () => {
|
||||
const wrapper = mountComponent();
|
||||
await flushPromises();
|
||||
@@ -84,7 +77,7 @@ describe('EnvironmentTabBar', () => {
|
||||
expect(envApi.listEnvironments).toHaveBeenCalledWith(mockProject.id);
|
||||
});
|
||||
|
||||
it('ThenEnvironmentChipsAreRendered', async () => {
|
||||
it('ThenEnvironmentSelectIsEnabled', async () => {
|
||||
jest.mocked(envApi.listEnvironments).mockResolvedValue(mockEnvironments);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
@@ -93,8 +86,7 @@ describe('EnvironmentTabBar', () => {
|
||||
contextStore.setProject(mockProject);
|
||||
await flushPromises();
|
||||
|
||||
expect(wrapper.find('[data-testid="env-chip-development"]').exists()).toBe(true);
|
||||
expect(wrapper.find('[data-testid="env-chip-production"]').exists()).toBe(true);
|
||||
expect(wrapper.find('[data-testid="env-select"]').attributes('disabled')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('ThenFirstEnvironmentIsAutoSelected', async () => {
|
||||
@@ -122,7 +114,7 @@ describe('EnvironmentTabBar', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenEnvironmentChipIsClicked', () => {
|
||||
describe('WhenEnvironmentIsSelected', () => {
|
||||
it('ThenContextStoreIsUpdatedWithSelectedEnvironment', async () => {
|
||||
jest.mocked(envApi.listEnvironments).mockResolvedValue(mockEnvironments);
|
||||
|
||||
@@ -132,7 +124,7 @@ describe('EnvironmentTabBar', () => {
|
||||
contextStore.setProject(mockProject);
|
||||
await flushPromises();
|
||||
|
||||
await wrapper.findComponent({ name: 'VChipGroup' }).vm.$emit('update:modelValue', 1);
|
||||
await wrapper.findComponent({ name: 'VSelect' }).vm.$emit('update:modelValue', mockEnvironments[1]);
|
||||
|
||||
expect(contextStore.currentEnvironment).toEqual(mockEnvironments[1]);
|
||||
});
|
||||
@@ -158,16 +150,49 @@ describe('EnvironmentTabBar', () => {
|
||||
});
|
||||
|
||||
describe('WhenApiReturnsNoEnvironments', () => {
|
||||
it('ThenEmptyMessageIsDisplayed', async () => {
|
||||
it('ThenNoEnvironmentIsAutoSelected', async () => {
|
||||
jest.mocked(envApi.listEnvironments).mockResolvedValue([]);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
const wrapper = mountComponent();
|
||||
mountComponent();
|
||||
|
||||
contextStore.setProject(mockProject);
|
||||
await flushPromises();
|
||||
|
||||
expect(wrapper.find('[data-testid="env-empty-message"]').exists()).toBe(true);
|
||||
expect(contextStore.currentEnvironment).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenStoredEnvironmentExistsInContextStore', () => {
|
||||
it('ThenStoredEnvironmentIsRestoredAfterFetch', async () => {
|
||||
jest.mocked(envApi.listEnvironments).mockResolvedValue(mockEnvironments);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setProject(mockProject);
|
||||
contextStore.setEnvironment(mockEnvironments[1]);
|
||||
|
||||
mountComponent();
|
||||
await flushPromises();
|
||||
|
||||
expect(contextStore.currentEnvironment).toEqual(mockEnvironments[1]);
|
||||
});
|
||||
|
||||
it('ThenEnvironmentIsClearedWhenProjectChanges', async () => {
|
||||
jest.mocked(envApi.listEnvironments).mockResolvedValue(mockEnvironments);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setProject(mockProject);
|
||||
contextStore.setEnvironment(mockEnvironments[0]);
|
||||
|
||||
mountComponent();
|
||||
await flushPromises();
|
||||
|
||||
const newProject: ProjectResponse = { ...mockProject, id: 20, name: 'Mobile' };
|
||||
jest.mocked(envApi.listEnvironments).mockResolvedValue([]);
|
||||
contextStore.setProject(newProject);
|
||||
await flushPromises();
|
||||
|
||||
expect(contextStore.currentEnvironment).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
6
src/admin/tests/unit/components/OrgSelector.spec.ts
Normal file → Executable file
6
src/admin/tests/unit/components/OrgSelector.spec.ts
Normal file → Executable file
@@ -18,8 +18,8 @@ import * as orgsApi from '@/api/organizations';
|
||||
const dialogStub = { template: '<div><slot /></div>' };
|
||||
|
||||
const mockOrgs: OrganizationResponse[] = [
|
||||
{ id: 1, name: 'Acme Corp', createdAt: '2026-01-01T00:00:00Z' },
|
||||
{ id: 2, name: 'Globex', createdAt: '2026-01-01T00:00:00Z' },
|
||||
{ id: 1, name: 'Acme Corp', createdAt: '2026-01-01T00:00:00Z', isPrimary: false },
|
||||
{ id: 2, name: 'Globex', createdAt: '2026-01-01T00:00:00Z', isPrimary: false },
|
||||
];
|
||||
|
||||
function mountComponent() {
|
||||
@@ -132,7 +132,7 @@ describe('OrgSelector', () => {
|
||||
});
|
||||
|
||||
describe('WhenCreateOrganizationIsConfirmed', () => {
|
||||
const newOrg: OrganizationResponse = { id: 3, name: 'New Org', createdAt: '2026-04-13T00:00:00Z' };
|
||||
const newOrg: OrganizationResponse = { id: 3, name: 'New Org', createdAt: '2026-04-13T00:00:00Z', isPrimary: false };
|
||||
|
||||
it('ThenCreateOrganizationApiIsCalled', async () => {
|
||||
jest.mocked(orgsApi.listOrganizations).mockResolvedValue([]);
|
||||
|
||||
52
src/admin/tests/unit/components/ProjectSelector.spec.ts
Normal file → Executable file
52
src/admin/tests/unit/components/ProjectSelector.spec.ts
Normal file → Executable file
@@ -17,7 +17,7 @@ import * as projectsApi from '@/api/projects';
|
||||
|
||||
const dialogStub = { template: '<div><slot /></div>' };
|
||||
|
||||
const mockOrg: OrganizationResponse = { id: 1, name: 'Acme', createdAt: '2026-01-01T00:00:00Z' };
|
||||
const mockOrg: OrganizationResponse = { id: 1, name: 'Acme', createdAt: '2026-01-01T00:00:00Z', isPrimary: false };
|
||||
const mockProjects: ProjectResponse[] = [
|
||||
{ id: 10, name: 'Website', organizationId: 1, hideDisabledFlags: false, createdAt: '2026-01-01T00:00:00Z' },
|
||||
{ id: 11, name: 'Mobile App', organizationId: 1, hideDisabledFlags: false, createdAt: '2026-01-01T00:00:00Z' },
|
||||
@@ -104,6 +104,54 @@ describe('ProjectSelector', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenStoredProjectExistsInContextStore', () => {
|
||||
it('ThenStoredProjectIsRestoredAfterFetch', async () => {
|
||||
jest.mocked(projectsApi.listProjects).mockResolvedValue(mockProjects);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization(mockOrg);
|
||||
contextStore.setProject(mockProjects[1]);
|
||||
|
||||
mountComponent();
|
||||
await flushPromises();
|
||||
|
||||
expect(contextStore.currentProject).toEqual(mockProjects[1]);
|
||||
});
|
||||
|
||||
it('ThenProjectIsClearedWhenNotFoundInNewOrg', async () => {
|
||||
jest.mocked(projectsApi.listProjects).mockResolvedValue(mockProjects);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization(mockOrg);
|
||||
contextStore.setProject(mockProjects[0]);
|
||||
|
||||
mountComponent();
|
||||
await flushPromises();
|
||||
|
||||
const newOrg: OrganizationResponse = { id: 2, name: 'Globex', createdAt: '2026-01-01T00:00:00Z', isPrimary: false };
|
||||
jest.mocked(projectsApi.listProjects).mockResolvedValue([]);
|
||||
contextStore.setOrganization(newOrg);
|
||||
await flushPromises();
|
||||
|
||||
expect(contextStore.currentProject).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenSingleProjectIsAvailable', () => {
|
||||
it('ThenItIsAutoSelected', async () => {
|
||||
const singleProject = mockProjects[0];
|
||||
jest.mocked(projectsApi.listProjects).mockResolvedValue([singleProject]);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
mountComponent();
|
||||
|
||||
contextStore.setOrganization(mockOrg);
|
||||
await flushPromises();
|
||||
|
||||
expect(contextStore.currentProject).toEqual(singleProject);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenOrganizationChanges', () => {
|
||||
it('ThenProjectsAreReloaded', async () => {
|
||||
jest.mocked(projectsApi.listProjects).mockResolvedValue(mockProjects);
|
||||
@@ -114,7 +162,7 @@ describe('ProjectSelector', () => {
|
||||
contextStore.setOrganization(mockOrg);
|
||||
await flushPromises();
|
||||
|
||||
const newOrg: OrganizationResponse = { id: 2, name: 'Globex', createdAt: '2026-01-01T00:00:00Z' };
|
||||
const newOrg: OrganizationResponse = { id: 2, name: 'Globex', createdAt: '2026-01-01T00:00:00Z', isPrimary: false };
|
||||
contextStore.setOrganization(newOrg);
|
||||
await flushPromises();
|
||||
|
||||
|
||||
2
src/admin/tests/unit/components/features/FeatureDetail.spec.ts
Normal file → Executable file
2
src/admin/tests/unit/components/features/FeatureDetail.spec.ts
Normal file → Executable file
@@ -46,7 +46,7 @@ describe('FeatureDetail', () => {
|
||||
setActivePinia(createPinia());
|
||||
jest.clearAllMocks();
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
contextStore.setEnvironment({ id: 100, name: 'Development', apiKey: 'env-dev', projectId: 10, createdAt: '' });
|
||||
});
|
||||
|
||||
2
src/admin/tests/unit/components/features/FeatureDialog.spec.ts
Normal file → Executable file
2
src/admin/tests/unit/components/features/FeatureDialog.spec.ts
Normal file → Executable file
@@ -39,7 +39,7 @@ describe('FeatureDialog', () => {
|
||||
setActivePinia(createPinia());
|
||||
jest.clearAllMocks();
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
});
|
||||
|
||||
|
||||
183
src/admin/tests/unit/components/features/TagsChipInput.spec.ts
Normal file → Executable file
183
src/admin/tests/unit/components/features/TagsChipInput.spec.ts
Normal file → Executable file
@@ -1,12 +1,13 @@
|
||||
import { describe, it, expect, beforeEach, jest } from '@jest/globals';
|
||||
import { mount, flushPromises, type VueWrapper } from '@vue/test-utils';
|
||||
import { mount, flushPromises, DOMWrapper, type VueWrapper } from '@vue/test-utils';
|
||||
import { setActivePinia, createPinia } from 'pinia';
|
||||
import { createRouter, createMemoryHistory } from 'vue-router';
|
||||
import TagsChipInput from '@/components/features/TagsChipInput.vue';
|
||||
import { useContextStore } from '@/stores/context';
|
||||
import type { ProjectResponse, TagResponse } from '@/types/api';
|
||||
import type { ProjectResponse, TagResponse, FeatureResponse } from '@/types/api';
|
||||
|
||||
jest.mock('@/api/tags');
|
||||
jest.mock('@/api/features');
|
||||
jest.mock('@/api/client', () => ({
|
||||
setAuthTokens: jest.fn(),
|
||||
clearAuthTokens: jest.fn(),
|
||||
@@ -14,18 +15,25 @@ jest.mock('@/api/client', () => ({
|
||||
}));
|
||||
|
||||
import * as tagsApi from '@/api/tags';
|
||||
import * as featuresApi from '@/api/features';
|
||||
|
||||
const mockProject: ProjectResponse = {
|
||||
id: 10, name: 'Website', organizationId: 1, hideDisabledFlags: false, createdAt: '2026-01-01T00:00:00Z',
|
||||
};
|
||||
const mockTags: TagResponse[] = [
|
||||
{ id: 1, label: 'backend', color: '#ff0000', projectId: 10 },
|
||||
{ id: 2, label: 'frontend', color: '#0000ff', projectId: 10 },
|
||||
];
|
||||
const backendTag: TagResponse = { id: 1, label: 'backend', color: '#ff0000', projectId: 10 };
|
||||
const frontendTag: TagResponse = { id: 2, label: 'frontend', color: '#0000ff', projectId: 10 };
|
||||
const mockTags: TagResponse[] = [backendTag, frontendTag];
|
||||
|
||||
function mountComponent() {
|
||||
function mockFeature(tags: TagResponse[]): FeatureResponse {
|
||||
return {
|
||||
id: 1, name: 'dark_mode', type: 'STANDARD', initialValue: null, description: null,
|
||||
defaultEnabled: false, projectId: 10, createdAt: '2026-01-01T00:00:00Z', tags,
|
||||
};
|
||||
}
|
||||
|
||||
function mountComponent(feature: FeatureResponse) {
|
||||
const router = createRouter({ history: createMemoryHistory(), routes: [{ path: '/', component: { template: '<div />' } }] });
|
||||
return mount(TagsChipInput, { global: { plugins: [router] } });
|
||||
return mount(TagsChipInput, { props: { feature }, global: { plugins: [router] } });
|
||||
}
|
||||
|
||||
describe('TagsChipInput', () => {
|
||||
@@ -33,107 +41,180 @@ describe('TagsChipInput', () => {
|
||||
setActivePinia(createPinia());
|
||||
jest.clearAllMocks();
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
});
|
||||
|
||||
describe('WhenComponentMounts', () => {
|
||||
it('ThenTagsAreFetchedFromApi', async () => {
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue(mockTags);
|
||||
mountComponent();
|
||||
mountComponent(mockFeature([]));
|
||||
await flushPromises();
|
||||
expect(tagsApi.listTags).toHaveBeenCalledWith(mockProject.id);
|
||||
});
|
||||
|
||||
it('ThenExistingTagChipsAreRendered', async () => {
|
||||
it('ThenAssignedTagChipsAreRendered', async () => {
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue(mockTags);
|
||||
const wrapper = mountComponent();
|
||||
const wrapper = mountComponent(mockFeature([backendTag]));
|
||||
await flushPromises();
|
||||
|
||||
expect(wrapper.find('[data-testid="tag-chip-backend"]').exists()).toBe(true);
|
||||
expect(wrapper.find('[data-testid="tag-chip-frontend"]').exists()).toBe(true);
|
||||
expect(wrapper.find('[data-testid="tag-chip-frontend"]').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('ThenEmptyMessageIsShownWhenNoTagsExist', async () => {
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue([]);
|
||||
const wrapper = mountComponent();
|
||||
it('ThenEmptyMessageIsShownWhenNoTagsAssigned', async () => {
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue(mockTags);
|
||||
const wrapper = mountComponent(mockFeature([]));
|
||||
await flushPromises();
|
||||
|
||||
expect(wrapper.find('[data-testid="tags-empty-message"]').exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenCreateTagIsSubmitted', () => {
|
||||
it('ThenCreateTagApiIsCalled', async () => {
|
||||
describe('WhenAddingANewTagName', () => {
|
||||
it('ThenCreateTagAndAssignAreBothCalled', async () => {
|
||||
const newTag: TagResponse = { id: 3, label: 'api', color: '#00ff00', projectId: 10 };
|
||||
const updatedFeature = mockFeature([newTag]);
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue(mockTags);
|
||||
jest.mocked(tagsApi.createTag).mockResolvedValue(newTag);
|
||||
jest.mocked(featuresApi.assignFeatureTag).mockResolvedValue(updatedFeature);
|
||||
|
||||
const wrapper = mountComponent();
|
||||
const wrapper = mountComponent(mockFeature([]));
|
||||
await flushPromises();
|
||||
|
||||
// Open the create form
|
||||
await wrapper.find('[data-testid="create-tag-btn"]').trigger('click');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Fill in the label
|
||||
await (wrapper.findComponent('[data-testid="new-tag-label-input"]') as VueWrapper<any>).vm.$emit('update:modelValue', 'api');
|
||||
|
||||
// Submit
|
||||
await wrapper.find('[data-testid="confirm-create-tag-btn"]').trigger('click');
|
||||
const input = wrapper.findComponent('[data-testid="tag-name-input"]') as VueWrapper<any>;
|
||||
await input.vm.$emit('update:modelValue', 'api');
|
||||
await wrapper.find('[data-testid="add-tag-btn"]').trigger('click');
|
||||
await flushPromises();
|
||||
|
||||
expect(tagsApi.createTag).toHaveBeenCalledWith(
|
||||
mockProject.id,
|
||||
expect.objectContaining({ label: 'api' }),
|
||||
);
|
||||
expect(tagsApi.createTag).toHaveBeenCalledWith(mockProject.id, expect.objectContaining({ label: 'api' }));
|
||||
expect(featuresApi.assignFeatureTag).toHaveBeenCalledWith(mockProject.id, 1, newTag.id);
|
||||
});
|
||||
|
||||
it('ThenNewTagAppearsInTheList', async () => {
|
||||
it('ThenUpdatedEventIsEmitted', async () => {
|
||||
const newTag: TagResponse = { id: 3, label: 'api', color: '#00ff00', projectId: 10 };
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue([]);
|
||||
const updatedFeature = mockFeature([newTag]);
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue(mockTags);
|
||||
jest.mocked(tagsApi.createTag).mockResolvedValue(newTag);
|
||||
jest.mocked(featuresApi.assignFeatureTag).mockResolvedValue(updatedFeature);
|
||||
|
||||
const wrapper = mountComponent();
|
||||
const wrapper = mountComponent(mockFeature([]));
|
||||
await flushPromises();
|
||||
|
||||
await wrapper.find('[data-testid="create-tag-btn"]').trigger('click');
|
||||
await wrapper.vm.$nextTick();
|
||||
await (wrapper.findComponent('[data-testid="new-tag-label-input"]') as VueWrapper<any>).vm.$emit('update:modelValue', 'api');
|
||||
await wrapper.find('[data-testid="confirm-create-tag-btn"]').trigger('click');
|
||||
const input = wrapper.findComponent('[data-testid="tag-name-input"]') as VueWrapper<any>;
|
||||
await input.vm.$emit('update:modelValue', 'api');
|
||||
await wrapper.find('[data-testid="add-tag-btn"]').trigger('click');
|
||||
await flushPromises();
|
||||
|
||||
expect(wrapper.find('[data-testid="tag-chip-api"]').exists()).toBe(true);
|
||||
expect(wrapper.emitted('updated')?.[0]).toEqual([updatedFeature]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenTagChipIsDeleted', () => {
|
||||
it('ThenDeleteTagApiIsCalled', async () => {
|
||||
describe('WhenTypedNameMatchesAnExistingTag', () => {
|
||||
it('ThenExistingTagIsAssignedInsteadOfCreatingANewOne', async () => {
|
||||
const updatedFeature = mockFeature([backendTag]);
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue(mockTags);
|
||||
jest.mocked(tagsApi.deleteTag).mockResolvedValue(undefined);
|
||||
jest.mocked(featuresApi.assignFeatureTag).mockResolvedValue(updatedFeature);
|
||||
|
||||
const wrapper = mountComponent();
|
||||
const wrapper = mountComponent(mockFeature([]));
|
||||
await flushPromises();
|
||||
|
||||
const chip = wrapper.findComponent('[data-testid="tag-chip-backend"]') as VueWrapper<any>;
|
||||
await chip.vm.$emit('click:close');
|
||||
const input = wrapper.findComponent('[data-testid="tag-name-input"]') as VueWrapper<any>;
|
||||
await input.vm.$emit('update:modelValue', 'backend');
|
||||
await wrapper.find('[data-testid="add-tag-btn"]').trigger('click');
|
||||
await flushPromises();
|
||||
|
||||
expect(tagsApi.deleteTag).toHaveBeenCalledWith(mockProject.id, mockTags[0].id);
|
||||
expect(tagsApi.createTag).not.toHaveBeenCalled();
|
||||
expect(featuresApi.assignFeatureTag).toHaveBeenCalledWith(mockProject.id, 1, backendTag.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenTypingTwoOrMoreCharacters', () => {
|
||||
it('ThenMatchingUnassignedTagsAreSuggested', async () => {
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue(mockTags);
|
||||
|
||||
const wrapper = mountComponent(mockFeature([]));
|
||||
await flushPromises();
|
||||
|
||||
const input = wrapper.findComponent('[data-testid="tag-name-input"]') as VueWrapper<any>;
|
||||
await input.vm.$emit('update:modelValue', 'ba');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(input.props('items')).toEqual(['backend']);
|
||||
});
|
||||
|
||||
it('ThenDeletedTagIsRemovedFromTheList', async () => {
|
||||
it('ThenNoSuggestionsAreShownBelowTwoCharacters', async () => {
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue(mockTags);
|
||||
jest.mocked(tagsApi.deleteTag).mockResolvedValue(undefined);
|
||||
|
||||
const wrapper = mountComponent();
|
||||
const wrapper = mountComponent(mockFeature([]));
|
||||
await flushPromises();
|
||||
|
||||
const input = wrapper.findComponent('[data-testid="tag-name-input"]') as VueWrapper<any>;
|
||||
await input.vm.$emit('update:modelValue', 'b');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(input.props('items')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenChoosingATagColour', () => {
|
||||
it('ThenSwatchClickOpensTheColourDialog', async () => {
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue(mockTags);
|
||||
const wrapper = mountComponent(mockFeature([]));
|
||||
await flushPromises();
|
||||
|
||||
expect(wrapper.find('[data-testid="color-dialog"]').exists()).toBe(false);
|
||||
await wrapper.find('[data-testid="tag-color-swatch"]').trigger('click');
|
||||
await flushPromises();
|
||||
|
||||
expect(wrapper.findComponent({ name: 'VDialog' }).props('modelValue')).toBe(true);
|
||||
});
|
||||
|
||||
it('ThenFifteenPresetColoursAreOffered', async () => {
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue(mockTags);
|
||||
const wrapper = mountComponent(mockFeature([]));
|
||||
await flushPromises();
|
||||
|
||||
await wrapper.find('[data-testid="tag-color-swatch"]').trigger('click');
|
||||
await flushPromises();
|
||||
|
||||
const presets = new DOMWrapper(document.body).findAll('[data-testid^="preset-color-"]');
|
||||
const distinctColors = new Set(presets.map((p) => p.attributes('data-testid')));
|
||||
expect(distinctColors.size).toBe(15);
|
||||
});
|
||||
|
||||
it('ThenSelectingAPresetUpdatesTheSwatchAndClosesTheDialog', async () => {
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue(mockTags);
|
||||
const wrapper = mountComponent(mockFeature([]));
|
||||
await flushPromises();
|
||||
|
||||
await wrapper.find('[data-testid="tag-color-swatch"]').trigger('click');
|
||||
await flushPromises();
|
||||
|
||||
const presets = new DOMWrapper(document.body).findAll('[data-testid="preset-color-#4CAF50"]');
|
||||
await presets[presets.length - 1].trigger('click');
|
||||
await wrapper.vm.$nextTick();
|
||||
await flushPromises();
|
||||
|
||||
expect(wrapper.find('[data-testid="tag-color-swatch"]').attributes('style')).toContain('background: rgb(76, 175, 80)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenTagChipIsClosed', () => {
|
||||
it('ThenRemoveFeatureTagApiIsCalled', async () => {
|
||||
const updatedFeature = mockFeature([]);
|
||||
jest.mocked(tagsApi.listTags).mockResolvedValue(mockTags);
|
||||
jest.mocked(featuresApi.removeFeatureTag).mockResolvedValue(updatedFeature);
|
||||
|
||||
const wrapper = mountComponent(mockFeature([backendTag]));
|
||||
await flushPromises();
|
||||
|
||||
const chip = wrapper.findComponent('[data-testid="tag-chip-backend"]') as VueWrapper<any>;
|
||||
await chip.vm.$emit('click:close');
|
||||
await flushPromises();
|
||||
|
||||
expect(wrapper.find('[data-testid="tag-chip-backend"]').exists()).toBe(false);
|
||||
expect(featuresApi.removeFeatureTag).toHaveBeenCalledWith(mockProject.id, 1, backendTag.id);
|
||||
expect(wrapper.emitted('updated')?.[0]).toEqual([updatedFeature]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
2
src/admin/tests/unit/components/identities/IdentityDetail.spec.ts
Normal file → Executable file
2
src/admin/tests/unit/components/identities/IdentityDetail.spec.ts
Normal file → Executable file
@@ -60,7 +60,7 @@ describe('IdentityDetail', () => {
|
||||
setActivePinia(createPinia());
|
||||
jest.clearAllMocks();
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
contextStore.setEnvironment(mockEnv);
|
||||
|
||||
|
||||
2
src/admin/tests/unit/components/segments/SegmentEditor.spec.ts
Normal file → Executable file
2
src/admin/tests/unit/components/segments/SegmentEditor.spec.ts
Normal file → Executable file
@@ -42,7 +42,7 @@ describe('SegmentEditor', () => {
|
||||
setActivePinia(createPinia());
|
||||
jest.clearAllMocks();
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
});
|
||||
|
||||
|
||||
0
src/admin/tests/unit/components/settings/WebhookDeliveryHistory.spec.ts
Normal file → Executable file
0
src/admin/tests/unit/components/settings/WebhookDeliveryHistory.spec.ts
Normal file → Executable file
0
src/admin/tests/unit/components/settings/WebhookDialog.spec.ts
Normal file → Executable file
0
src/admin/tests/unit/components/settings/WebhookDialog.spec.ts
Normal file → Executable file
0
src/admin/tests/unit/components/settings/WebhooksPanel.spec.ts
Normal file → Executable file
0
src/admin/tests/unit/components/settings/WebhooksPanel.spec.ts
Normal file → Executable file
0
src/admin/tests/unit/router/router.spec.ts
Normal file → Executable file
0
src/admin/tests/unit/router/router.spec.ts
Normal file → Executable file
0
src/admin/tests/unit/stores/auth.spec.ts
Normal file → Executable file
0
src/admin/tests/unit/stores/auth.spec.ts
Normal file → Executable file
53
src/admin/tests/unit/stores/context.spec.ts
Normal file → Executable file
53
src/admin/tests/unit/stores/context.spec.ts
Normal file → Executable file
@@ -3,7 +3,7 @@ import { setActivePinia, createPinia } from 'pinia';
|
||||
import { useContextStore } from '@/stores/context';
|
||||
import type { OrganizationResponse, ProjectResponse, EnvironmentResponse } from '@/types/api';
|
||||
|
||||
const mockOrg: OrganizationResponse = { id: 1, name: 'Acme', createdAt: '2026-01-01T00:00:00Z' };
|
||||
const mockOrg: OrganizationResponse = { id: 1, name: 'Acme', createdAt: '2026-01-01T00:00:00Z', isPrimary: false };
|
||||
const mockProject: ProjectResponse = {
|
||||
id: 10,
|
||||
name: 'Website',
|
||||
@@ -119,6 +119,57 @@ describe('useContextStore', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenRefreshOrganizationIsCalled', () => {
|
||||
it('ThenOrgIsUpdatedWithoutClearingProjectOrEnvironment', () => {
|
||||
const store = useContextStore();
|
||||
store.setOrganization(mockOrg);
|
||||
store.setProject(mockProject);
|
||||
store.setEnvironment(mockEnv);
|
||||
|
||||
const updatedOrg = { ...mockOrg, name: 'Acme Updated' };
|
||||
store.refreshOrganization(updatedOrg);
|
||||
|
||||
expect(store.currentOrganization).toEqual(updatedOrg);
|
||||
expect(store.currentProject).toEqual(mockProject);
|
||||
expect(store.currentEnvironment).toEqual(mockEnv);
|
||||
});
|
||||
|
||||
it('ThenOrgIsPersistedToLocalStorage', () => {
|
||||
const store = useContextStore();
|
||||
const updatedOrg = { ...mockOrg, name: 'Acme Updated' };
|
||||
store.refreshOrganization(updatedOrg);
|
||||
|
||||
const stored = JSON.parse(localStorage.getItem('mic_ctx_organization')!);
|
||||
expect(stored).toEqual(updatedOrg);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenRefreshProjectIsCalled', () => {
|
||||
it('ThenProjectIsUpdatedWithoutClearingEnvironment', () => {
|
||||
const store = useContextStore();
|
||||
store.setProject(mockProject);
|
||||
store.setEnvironment(mockEnv);
|
||||
|
||||
const updatedProject = { ...mockProject, name: 'Website Updated' };
|
||||
store.refreshProject(updatedProject);
|
||||
|
||||
expect(store.currentProject).toEqual(updatedProject);
|
||||
expect(store.currentEnvironment).toEqual(mockEnv);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenRefreshEnvironmentIsCalled', () => {
|
||||
it('ThenEnvironmentIsUpdatedAndPersisted', () => {
|
||||
const store = useContextStore();
|
||||
const updatedEnv = { ...mockEnv, name: 'Production Updated' };
|
||||
store.refreshEnvironment(updatedEnv);
|
||||
|
||||
expect(store.currentEnvironment).toEqual(updatedEnv);
|
||||
const stored = JSON.parse(localStorage.getItem('mic_ctx_environment')!);
|
||||
expect(stored).toEqual(updatedEnv);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenClearContextIsCalled', () => {
|
||||
it('ThenAllContextIsRemovedFromStateAndStorage', () => {
|
||||
const store = useContextStore();
|
||||
|
||||
18
src/admin/tests/unit/views/AuditLogsView.spec.ts
Normal file → Executable file
18
src/admin/tests/unit/views/AuditLogsView.spec.ts
Normal file → Executable file
@@ -23,12 +23,12 @@ const mockLogs: AuditLogResponse[] = [
|
||||
{
|
||||
id: 1, resourceType: 'Feature', resourceId: '42', action: 'Created',
|
||||
changes: null, organizationId: 1, projectId: 10, environmentId: null,
|
||||
actorUserId: 5, createdAt: '2026-01-10T10:00:00Z',
|
||||
actorUserId: 5, actorUserName: 'Alice', createdAt: '2026-01-10T10:00:00Z',
|
||||
},
|
||||
{
|
||||
id: 2, resourceType: 'FeatureState', resourceId: '99', action: 'Updated',
|
||||
changes: '{"enabled":true}', organizationId: 1, projectId: 10, environmentId: 100,
|
||||
actorUserId: 5, createdAt: '2026-01-11T11:00:00Z',
|
||||
actorUserId: 5, actorUserName: 'Alice', createdAt: '2026-01-11T11:00:00Z',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -58,7 +58,7 @@ describe('AuditLogsView', () => {
|
||||
});
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
mountView();
|
||||
@@ -76,7 +76,7 @@ describe('AuditLogsView', () => {
|
||||
});
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
@@ -95,7 +95,7 @@ describe('AuditLogsView', () => {
|
||||
});
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
@@ -120,7 +120,7 @@ describe('AuditLogsView', () => {
|
||||
});
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
@@ -145,7 +145,7 @@ describe('AuditLogsView', () => {
|
||||
});
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
@@ -170,7 +170,7 @@ describe('AuditLogsView', () => {
|
||||
});
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
@@ -195,7 +195,7 @@ describe('AuditLogsView', () => {
|
||||
});
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
|
||||
55
src/admin/tests/unit/views/FeaturesView.spec.ts
Normal file → Executable file
55
src/admin/tests/unit/views/FeaturesView.spec.ts
Normal file → Executable file
@@ -4,7 +4,7 @@ import { setActivePinia, createPinia } from 'pinia';
|
||||
import { createRouter, createMemoryHistory } from 'vue-router';
|
||||
import FeaturesView from '@/views/FeaturesView.vue';
|
||||
import { useContextStore } from '@/stores/context';
|
||||
import type { ProjectResponse, EnvironmentResponse, FeatureResponse, FeatureStateResponse } from '@/types/api';
|
||||
import type { ProjectResponse, EnvironmentResponse, FeatureResponse, FeatureStateResponse, TagResponse } from '@/types/api';
|
||||
|
||||
jest.mock('@/api/features');
|
||||
jest.mock('@/api/featureStates');
|
||||
@@ -25,9 +25,13 @@ const mockEnv: EnvironmentResponse = {
|
||||
id: 100, name: 'Development', apiKey: 'env-dev', projectId: 10, createdAt: '2026-01-01T00:00:00Z',
|
||||
};
|
||||
const mockFeatures: FeatureResponse[] = [
|
||||
{ id: 1, name: 'dark_mode', type: 'STANDARD', initialValue: null, description: 'Dark mode toggle', defaultEnabled: false, projectId: 10, createdAt: '2026-01-01T00:00:00Z' },
|
||||
{ id: 2, name: 'new_checkout', type: 'STANDARD', initialValue: null, description: null, defaultEnabled: true, projectId: 10, createdAt: '2026-01-01T00:00:00Z' },
|
||||
{ id: 1, name: 'dark_mode', type: 'STANDARD', initialValue: null, description: 'Dark mode toggle', defaultEnabled: false, projectId: 10, createdAt: '2026-01-01T00:00:00Z', tags: [] },
|
||||
{ id: 2, name: 'new_checkout', type: 'STANDARD', initialValue: null, description: null, defaultEnabled: true, projectId: 10, createdAt: '2026-01-01T00:00:00Z', tags: [] },
|
||||
];
|
||||
|
||||
function makeTag(id: number, label: string): TagResponse {
|
||||
return { id, label, color: '#1565C0', projectId: 10, createdAt: '2026-01-01T00:00:00Z' };
|
||||
}
|
||||
const mockStates: FeatureStateResponse[] = [
|
||||
{ id: 101, featureId: 1, environmentId: 100, identityId: null, featureSegmentId: null, enabled: false, value: null, createdAt: '2026-01-01T00:00:00Z', updatedAt: '2026-01-01T00:00:00Z' },
|
||||
{ id: 102, featureId: 2, environmentId: 100, identityId: null, featureSegmentId: null, enabled: true, value: null, createdAt: '2026-01-01T00:00:00Z', updatedAt: '2026-01-01T00:00:00Z' },
|
||||
@@ -58,7 +62,7 @@ describe('FeaturesView', () => {
|
||||
jest.mocked(featureStatesApi.listFeatureStates).mockResolvedValue(mockStates);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
contextStore.setEnvironment(mockEnv);
|
||||
|
||||
@@ -74,7 +78,7 @@ describe('FeaturesView', () => {
|
||||
jest.mocked(featureStatesApi.listFeatureStates).mockResolvedValue(mockStates);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
@@ -85,6 +89,43 @@ describe('FeaturesView', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenFeatureHasTags', () => {
|
||||
it('ThenUpToFiveTagChipsAreShownAndExtraAreEllipsised', async () => {
|
||||
const sixTags = [1, 2, 3, 4, 5, 6].map((id) => makeTag(id, `tag-${id}`));
|
||||
const taggedFeature: FeatureResponse = { ...mockFeatures[0], tags: sixTags };
|
||||
jest.mocked(featuresApi.listFeatures).mockResolvedValue({ count: 1, next: null, previous: null, results: [taggedFeature] });
|
||||
jest.mocked(featureStatesApi.listFeatureStates).mockResolvedValue([]);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
await flushPromises();
|
||||
|
||||
const chips = sixTags.slice(0, 5).map((tag) => wrapper.find(`[data-testid="feature-tag-chip-${taggedFeature.id}-${tag.id}"]`));
|
||||
chips.forEach((chip) => expect(chip.exists()).toBe(true));
|
||||
expect(wrapper.find(`[data-testid="feature-tag-chip-${taggedFeature.id}-6"]`).exists()).toBe(false);
|
||||
expect(wrapper.text()).toContain('…');
|
||||
});
|
||||
|
||||
it('ThenMissingTagsDoesNotThrow', async () => {
|
||||
const untaggedFeature = { ...mockFeatures[0], tags: undefined } as unknown as FeatureResponse;
|
||||
jest.mocked(featuresApi.listFeatures).mockResolvedValue({ count: 1, next: null, previous: null, results: [untaggedFeature] });
|
||||
jest.mocked(featureStatesApi.listFeatureStates).mockResolvedValue([]);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
await flushPromises();
|
||||
|
||||
expect(wrapper.find('[data-testid="features-table"]').exists()).toBe(true);
|
||||
expect(wrapper.text()).toContain(untaggedFeature.name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhenToggleEnabledIsClicked', () => {
|
||||
it('ThenPatchFeatureStateIsCalled', async () => {
|
||||
jest.mocked(featuresApi.listFeatures).mockResolvedValue({ count: 2, next: null, previous: null, results: mockFeatures });
|
||||
@@ -92,7 +133,7 @@ describe('FeaturesView', () => {
|
||||
jest.mocked(featureStatesApi.patchFeatureState).mockResolvedValue({ ...mockStates[0], enabled: true });
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
contextStore.setEnvironment(mockEnv);
|
||||
|
||||
@@ -115,7 +156,7 @@ describe('FeaturesView', () => {
|
||||
jest.mocked(featureStatesApi.listFeatureStates).mockResolvedValue([]);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
|
||||
12
src/admin/tests/unit/views/IdentitiesView.spec.ts
Normal file → Executable file
12
src/admin/tests/unit/views/IdentitiesView.spec.ts
Normal file → Executable file
@@ -57,7 +57,7 @@ describe('IdentitiesView', () => {
|
||||
});
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
contextStore.setEnvironment(mockEnv);
|
||||
|
||||
@@ -73,7 +73,7 @@ describe('IdentitiesView', () => {
|
||||
});
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
contextStore.setEnvironment(mockEnv);
|
||||
|
||||
@@ -91,7 +91,7 @@ describe('IdentitiesView', () => {
|
||||
});
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
contextStore.setEnvironment(mockEnv);
|
||||
|
||||
@@ -115,7 +115,7 @@ describe('IdentitiesView', () => {
|
||||
});
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
contextStore.setEnvironment(mockEnv);
|
||||
|
||||
@@ -137,7 +137,7 @@ describe('IdentitiesView', () => {
|
||||
});
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
contextStore.setEnvironment(mockEnv);
|
||||
|
||||
@@ -163,7 +163,7 @@ describe('IdentitiesView', () => {
|
||||
});
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
contextStore.setEnvironment(mockEnv);
|
||||
|
||||
|
||||
2
src/admin/tests/unit/views/ProfileView.spec.ts
Normal file → Executable file
2
src/admin/tests/unit/views/ProfileView.spec.ts
Normal file → Executable file
@@ -44,7 +44,7 @@ describe('ProfileView', () => {
|
||||
it('ThenOrgNameIsDisplayedWhenSelected', () => {
|
||||
const { wrapper } = mountView();
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme Corp', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme Corp', createdAt: '', isPrimary: false });
|
||||
|
||||
expect(wrapper.find('[data-testid="profile-org-name"]').exists()).toBe(false); // org name shown in v-if block
|
||||
});
|
||||
|
||||
18
src/admin/tests/unit/views/SegmentsView.spec.ts
Normal file → Executable file
18
src/admin/tests/unit/views/SegmentsView.spec.ts
Normal file → Executable file
@@ -61,7 +61,7 @@ describe('SegmentsView', () => {
|
||||
jest.mocked(segmentsApi.listSegments).mockResolvedValue(mockSegments);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
mountView();
|
||||
@@ -74,7 +74,7 @@ describe('SegmentsView', () => {
|
||||
jest.mocked(segmentsApi.listSegments).mockResolvedValue(mockSegments);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
@@ -88,7 +88,7 @@ describe('SegmentsView', () => {
|
||||
jest.mocked(segmentsApi.listSegments).mockResolvedValue(mockSegments);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
@@ -104,7 +104,7 @@ describe('SegmentsView', () => {
|
||||
jest.mocked(segmentsApi.listSegments).mockResolvedValue([]);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
@@ -123,7 +123,7 @@ describe('SegmentsView', () => {
|
||||
jest.mocked(segmentsApi.listSegments).mockResolvedValue(mockSegments);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
@@ -143,7 +143,7 @@ describe('SegmentsView', () => {
|
||||
jest.mocked(segmentsApi.deleteSegment).mockResolvedValue(undefined);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
@@ -172,7 +172,7 @@ describe('SegmentsView', () => {
|
||||
jest.mocked(segmentsApi.deleteSegment).mockResolvedValue(undefined);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
@@ -196,7 +196,7 @@ describe('SegmentsView', () => {
|
||||
jest.mocked(segmentsApi.listSegments).mockResolvedValue([]);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
@@ -214,7 +214,7 @@ describe('SegmentsView', () => {
|
||||
jest.mocked(segmentsApi.listSegments).mockResolvedValue([...mockSegments]);
|
||||
|
||||
const contextStore = useContextStore();
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '' });
|
||||
contextStore.setOrganization({ id: 1, name: 'Acme', createdAt: '', isPrimary: false });
|
||||
contextStore.setProject(mockProject);
|
||||
|
||||
const wrapper = mountView();
|
||||
|
||||
2
src/admin/tests/unit/views/SettingsView.spec.ts
Normal file → Executable file
2
src/admin/tests/unit/views/SettingsView.spec.ts
Normal file → Executable file
@@ -26,7 +26,7 @@ import * as environmentsApi from '@/api/environments';
|
||||
import * as apiKeysApi from '@/api/apiKeys';
|
||||
import * as webhooksApi from '@/api/webhooks';
|
||||
|
||||
const mockOrg: OrganizationResponse = { id: 1, name: 'Acme', createdAt: '2026-01-01T00:00:00Z' };
|
||||
const mockOrg: OrganizationResponse = { id: 1, name: 'Acme', createdAt: '2026-01-01T00:00:00Z', isPrimary: false };
|
||||
const mockProject: ProjectResponse = { id: 10, name: 'Website', organizationId: 1, hideDisabledFlags: false, createdAt: '2026-01-01T00:00:00Z' };
|
||||
const mockEnvs: EnvironmentResponse[] = [
|
||||
{ id: 100, name: 'Development', apiKey: 'env-dev', projectId: 10, createdAt: '2026-01-01T00:00:00Z' },
|
||||
|
||||
Reference in New Issue
Block a user