/** * ContactListItem Component Tests * * Comprehensive test suite for the ContactListItem component. * Tests component rendering, props, events, and user interactions. * * @author Matthew Raymer */ import { describe, it, expect, beforeEach } from "vitest"; import { mount } from "@vue/test-utils"; import ContactListItem from "@/components/ContactListItem.vue"; import { createStandardMockContact } from "@/test/factories/contactFactory"; import { createComponentWrapper, testLifecycleEvents, testPerformance, testAccessibility, testErrorHandling, } from "@/test/utils/componentTestUtils"; describe("ContactListItem", () => { let wrapper: any; beforeEach(() => { wrapper = null; }); const mountComponent = (props = {}) => { return mount(ContactListItem, { props: { contact: createStandardMockContact(), activeDid: "did:ethr:test:active", showCheckbox: false, showActions: false, isSelected: false, showGiveTotals: true, showGiveConfirmed: true, givenToMeDescriptions: {}, givenToMeConfirmed: {}, givenToMeUnconfirmed: {}, givenByMeDescriptions: {}, givenByMeConfirmed: {}, givenByMeUnconfirmed: {}, ...props, }, global: { stubs: { EntityIcon: { template: '
EntityIcon
', props: ["contact", "iconSize"], }, "font-awesome": { template: 'FontAwesome', }, }, }, }); }; describe("Component Rendering", () => { it("should render with correct structure when all props are provided", () => { wrapper = mountComponent(); expect(wrapper.exists()).toBe(true); expect(wrapper.find('[data-testid="contactListItem"]').exists()).toBe( true, ); expect(wrapper.find(".entity-icon-stub").exists()).toBe(true); expect(wrapper.find("h2").exists()).toBe(true); }); it("should display contact name correctly", () => { const contact = createStandardMockContact({ name: "Test Contact" }); wrapper = mountComponent({ contact }); expect( wrapper .find("h2") .text() .replace(/\u00A0/g, " "), ).toContain("Test Contact"); }); it("should display contact DID correctly", () => { const contact = createStandardMockContact({ did: "did:ethr:test:123" }); wrapper = mountComponent({ contact }); expect(wrapper.text()).toContain("did:ethr:test:123"); }); it("should display contact notes when available", () => { const contact = createStandardMockContact({ notes: "Test notes" }); wrapper = mountComponent({ contact }); expect(wrapper.text()).toContain("Test notes"); }); }); describe("Checkbox Functionality", () => { it("should show checkbox when showCheckbox is true", () => { wrapper = mountComponent({ showCheckbox: true }); expect(wrapper.find('[data-testid="contactCheckOne"]').exists()).toBe( true, ); }); it("should not show checkbox when showCheckbox is false", () => { wrapper = mountComponent({ showCheckbox: false }); expect(wrapper.find('[data-testid="contactCheckOne"]').exists()).toBe( false, ); }); it("should emit toggle-selection event when checkbox is clicked", () => { const contact = createStandardMockContact({ did: "did:ethr:test:123" }); wrapper = mountComponent({ showCheckbox: true, contact }); wrapper.find('[data-testid="contactCheckOne"]').trigger("click"); expect(wrapper.emitted("toggle-selection")).toBeTruthy(); expect(wrapper.emitted("toggle-selection")[0]).toEqual([ "did:ethr:test:123", ]); }); it("should reflect isSelected prop in checkbox state", () => { wrapper = mountComponent({ showCheckbox: true, isSelected: true }); const checkbox = wrapper.find('[data-testid="contactCheckOne"]'); expect(checkbox.attributes("checked")).toBeDefined(); }); }); describe("Actions Section", () => { it("should show actions when showActions is true and contact is not active", () => { wrapper = mountComponent({ showActions: true, contact: createStandardMockContact({ did: "did:ethr:test:other" }), }); expect(wrapper.find('[data-testid="offerButton"]').exists()).toBe(true); }); it("should not show actions when contact is active", () => { const contact = createStandardMockContact({ did: "did:ethr:test:active", }); wrapper = mountComponent({ showActions: true, contact, activeDid: "did:ethr:test:active", }); expect(wrapper.find('[data-testid="offerButton"]').exists()).toBe(false); }); it("should emit show-identicon event when EntityIcon is clicked", () => { const contact = createStandardMockContact(); wrapper = mountComponent({ contact }); wrapper.find(".entity-icon-stub").trigger("click"); expect(wrapper.emitted("show-identicon")).toBeTruthy(); expect(wrapper.emitted("show-identicon")[0]).toEqual([contact]); }); it("should emit open-offer-dialog event when offer button is clicked", () => { wrapper = mountComponent({ showActions: true, contact: createStandardMockContact({ did: "did:ethr:test:other" }), }); wrapper.find('[data-testid="offerButton"]').trigger("click"); expect(wrapper.emitted("open-offer-dialog")).toBeTruthy(); expect(wrapper.emitted("open-offer-dialog")[0][0]).toBe( "did:ethr:test:other", ); }); }); describe("Give Amounts Display", () => { it("should display give amounts correctly for given to me", () => { const contact = createStandardMockContact({ did: "did:ethr:test:123" }); wrapper = mountComponent({ contact, showActions: true, givenToMeConfirmed: { "did:ethr:test:123": 50 }, givenToMeUnconfirmed: { "did:ethr:test:123": 25 }, }); const buttons = wrapper.findAll("button"); if (buttons.length > 0) { expect(buttons[0].text()).toBe("75"); // 50 + 25 } }); it("should display give amounts correctly for given by me", () => { const contact = createStandardMockContact({ did: "did:ethr:test:123" }); wrapper = mountComponent({ contact, showActions: true, givenByMeConfirmed: { "did:ethr:test:123": 30 }, givenByMeUnconfirmed: { "did:ethr:test:123": 20 }, }); const buttons = wrapper.findAll("button"); if (buttons.length > 1) { expect(buttons[1].text()).toBe("50"); // 30 + 20 } }); it("should show only confirmed amounts when showGiveConfirmed is true", () => { const contact = createStandardMockContact({ did: "did:ethr:test:123" }); wrapper = mountComponent({ contact, showActions: true, showGiveTotals: false, showGiveConfirmed: true, givenToMeConfirmed: { "did:ethr:test:123": 50 }, givenToMeUnconfirmed: { "did:ethr:test:123": 25 }, }); const buttons = wrapper.findAll("button"); if (buttons.length > 0) { expect(buttons[0].text()).toBe("50"); // Only confirmed } }); it("should show only unconfirmed amounts when showGiveConfirmed is false", () => { const contact = createStandardMockContact({ did: "did:ethr:test:123" }); wrapper = mountComponent({ contact, showActions: true, showGiveTotals: false, showGiveConfirmed: false, givenToMeConfirmed: { "did:ethr:test:123": 50 }, givenToMeUnconfirmed: { "did:ethr:test:123": 25 }, }); const buttons = wrapper.findAll("button"); if (buttons.length > 0) { expect(buttons[0].text()).toBe("25"); // Only unconfirmed } }); }); describe("Error Handling", () => { it("should handle undefined contact name gracefully", () => { const contact = createStandardMockContact({ name: undefined }); wrapper = mountComponent({ contact }); expect( wrapper .find("h2") .text() .replace(/\u00A0/g, " "), ).toContain("(no name)"); }); it("should handle missing give amounts gracefully", () => { const contact = createStandardMockContact({ did: "did:ethr:test:123" }); wrapper = mountComponent({ contact, showActions: true, givenToMeConfirmed: {}, givenToMeUnconfirmed: {}, givenByMeConfirmed: {}, givenByMeUnconfirmed: {}, }); const buttons = wrapper.findAll("button"); if (buttons.length > 0) { expect(buttons[0].text()).toBe("0"); } if (buttons.length > 1) { expect(buttons[1].text()).toBe("0"); } }); it("should handle rapid prop changes gracefully", () => { wrapper = mountComponent(); for (let i = 0; i < 10; i++) { wrapper.setProps({ isSelected: i % 2 === 0, showCheckbox: i % 3 === 0, showActions: i % 4 === 0, }); } expect(wrapper.exists()).toBe(true); }); }); describe("Performance Testing", () => { it("should render within performance threshold", () => { const performanceResult = testPerformance(() => { mountComponent(); }, 50); expect(performanceResult.passed).toBe(true); expect(performanceResult.duration).toBeLessThan(50); }); it("should handle multiple re-renders efficiently", () => { wrapper = mountComponent(); const start = performance.now(); for (let i = 0; i < 50; i++) { wrapper.setProps({ isSelected: i % 2 === 0 }); } const end = performance.now(); expect(end - start).toBeLessThan(200); }); it("should establish performance baseline", () => { const start = performance.now(); wrapper = mountComponent(); const end = performance.now(); console.log("Performance Baseline:", { renderTime: end - start, }); expect(end - start).toBeLessThan(100); }); }); describe("Integration Testing", () => { it("should integrate with EntityIcon component correctly", () => { const contact = createStandardMockContact(); wrapper = mountComponent({ contact }); const entityIcon = wrapper.find(".entity-icon-stub"); expect(entityIcon.exists()).toBe(true); }); it("should handle multiple concurrent events", () => { wrapper = mountComponent({ showCheckbox: true, showActions: true }); // Simulate multiple rapid interactions wrapper.find('[data-testid="contactCheckOne"]').trigger("click"); wrapper.find(".entity-icon-stub").trigger("click"); wrapper.find('[data-testid="offerButton"]').trigger("click"); expect(wrapper.emitted("toggle-selection")).toBeTruthy(); expect(wrapper.emitted("show-identicon")).toBeTruthy(); expect(wrapper.emitted("open-offer-dialog")).toBeTruthy(); }); }); describe("Snapshot Testing", () => { it("should maintain consistent DOM structure", () => { wrapper = mountComponent(); const html = wrapper.html(); expect(html).toMatch(/]*class="[^"]*border-b[^"]*"[^>]*>/); expect(html).toMatch(/]*class="[^"]*flex[^"]*"[^>]*>/); expect(html).toContain("EntityIcon"); expect(html).toContain('data-testid="contactListItem"'); }); it("should maintain consistent structure with different prop combinations", () => { const propCombinations = [ { showCheckbox: true, showActions: false }, { showCheckbox: false, showActions: true }, { showCheckbox: true, showActions: true }, { showCheckbox: false, showActions: false }, ]; propCombinations.forEach((props) => { const testWrapper = mountComponent(props); const html = testWrapper.html(); expect(html).toMatch(/]*class="[^"]*border-b[^"]*"[^>]*>/); expect(html).toContain("EntityIcon"); if (props.showCheckbox) { expect(html).toContain('data-testid="contactCheckOne"'); } else { expect(html).not.toContain('data-testid="contactCheckOne"'); } }); }); }); describe("Accessibility Testing", () => { it("should meet WCAG accessibility standards", () => { wrapper = mountComponent(); const listItem = wrapper.find('[data-testid="contactListItem"]'); const checkbox = wrapper.find('[data-testid="contactCheckOne"]'); const offerButton = wrapper.find('[data-testid="offerButton"]'); // Semantic structure expect(listItem.exists()).toBe(true); expect(listItem.element.tagName.toLowerCase()).toBe("li"); // Form control accessibility if (checkbox.exists()) { expect(checkbox.attributes("type")).toBe("checkbox"); } // Button accessibility if (offerButton.exists()) { expect(offerButton.text()).toBe("Offer"); } }); it("should support keyboard navigation", () => { wrapper = mountComponent({ showCheckbox: true, showActions: true }); const checkbox = wrapper.find('[data-testid="contactCheckOne"]'); const offerButton = wrapper.find('[data-testid="offerButton"]'); // Test that controls are clickable (supports keyboard navigation) expect(checkbox.exists()).toBe(true); expect(offerButton.exists()).toBe(true); checkbox.trigger("click"); expect(wrapper.emitted("toggle-selection")).toBeTruthy(); offerButton.trigger("click"); expect(wrapper.emitted("open-offer-dialog")).toBeTruthy(); }); it("should have descriptive content", () => { const contact = createStandardMockContact({ name: "Test Contact" }); wrapper = mountComponent({ contact }); expect(wrapper.text().replace(/\u00A0/g, " ")).toContain("Test Contact"); expect(wrapper.text()).toContain("did:ethr:test"); }); it("should maintain accessibility with different prop combinations", () => { const testCases = [ { showCheckbox: true, showActions: false }, { showCheckbox: false, showActions: true }, { showCheckbox: true, showActions: true }, ]; testCases.forEach((props) => { const testWrapper = mountComponent(props); const listItem = testWrapper.find('[data-testid="contactListItem"]'); expect(listItem.exists()).toBe(true); expect(testWrapper.find(".entity-icon-stub").exists()).toBe(true); }); }); }); describe("Centralized Utility Testing", () => { it("should use centralized component wrapper", () => { const wrapperFactory = createComponentWrapper(ContactListItem, { contact: createStandardMockContact(), activeDid: "did:ethr:test:active", showCheckbox: false, showActions: false, isSelected: false, showGiveTotals: true, showGiveConfirmed: true, givenToMeDescriptions: {}, givenToMeConfirmed: {}, givenToMeUnconfirmed: {}, givenByMeDescriptions: {}, givenByMeConfirmed: {}, givenByMeUnconfirmed: {}, }); const testWrapper = wrapperFactory(); expect(testWrapper.exists()).toBe(true); expect(testWrapper.find('[data-testid="contactListItem"]').exists()).toBe( true, ); }); it("should test lifecycle events using centralized utilities", async () => { wrapper = mountComponent(); const results = await testLifecycleEvents(wrapper, [ "mounted", "updated", ]); expect(results).toHaveLength(2); expect(results.every((r) => r.success)).toBe(true); }); it("should test performance using centralized utilities", () => { const performanceResult = testPerformance(() => { mountComponent(); }, 50); expect(performanceResult.passed).toBe(true); expect(performanceResult.duration).toBeLessThan(50); }); it("should test accessibility using centralized utilities", () => { wrapper = mountComponent(); const accessibilityChecks = [ { name: "has list item", test: (wrapper: any) => wrapper.find('[data-testid="contactListItem"]').exists(), }, { name: "has entity icon", test: (wrapper: any) => wrapper.find(".entity-icon-stub").exists(), }, { name: "has contact name", test: (wrapper: any) => wrapper.find("h2").exists(), }, ]; const results = testAccessibility(wrapper, accessibilityChecks); expect(results).toHaveLength(3); expect(results.every((r) => r.success && r.passed)).toBe(true); }); it("should test error handling using centralized utilities", async () => { wrapper = mountComponent(); const errorScenarios = [ { name: "invalid props", action: async (wrapper: any) => { await wrapper.setProps({ isSelected: "invalid" as any }); }, expectedBehavior: "should handle gracefully", }, ]; const results = await testErrorHandling(wrapper, errorScenarios); expect(results).toHaveLength(1); expect(results.every((r) => r.success)).toBe(true); }); }); });