diff --git a/src/App.vue b/src/App.vue index f4c5754aa..045a28bf8 100644 --- a/src/App.vue +++ b/src/App.vue @@ -7,6 +7,7 @@ + diff --git a/src/fmg-components/funnel-header/progress-bar/progres-bar.spec.js b/src/fmg-components/funnel-header/progress-bar/progres-bar.spec.js deleted file mode 100644 index 9970b266c..000000000 --- a/src/fmg-components/funnel-header/progress-bar/progres-bar.spec.js +++ /dev/null @@ -1,81 +0,0 @@ -import { shallowMount } from "@vue/test-utils"; -import progressBar from "./progress-bar"; -import store from "@/store"; - -describe("progressBar", () => { - test("progress should be 0", () => { - // Arrange - mockMixin.computed = { - pageName: () => undefined, - }; - - // Act - const wrapper = shallowMount(progressBar, { - mixins: [mockMixin], - }); - - // Assert - expect(wrapper.vm.progress).toBe(0); - wrapper.unmount(); - }); -}); - -describe("progressBar", () => { - test("progress should be 4%", () => { - // Arrange - mockMixin.computed = { - pageName: () => "vehicle", - }; - - // Act - const wrapper = shallowMount(progressBar, { - mixins: [mockMixin], - }); - - // Assert - expect(wrapper.vm.progress).toBe(4); - wrapper.unmount(); - }); -}); - -describe("progressBar", () => { - test("progress should be 48%", () => { - // Arrange - mockMixin.computed = { - pageName: () => "quote", - }; - - // Act - const wrapper = shallowMount(progressBar, { - mixins: [mockMixin], - }); - - // Assert - expect(wrapper.vm.progress).toBe(48); - wrapper.unmount(); - }); -}); - -describe("progressBar", () => { - test("progress should be 100%", () => { - // Arrange - mockMixin.computed = { - pageName: () => "confirmation", - }; - - // Act - const wrapper = shallowMount(progressBar, { - mixins: [mockMixin], - }); - - // Assert - expect(wrapper.vm.progress).toBe(100); - wrapper.unmount(); - }); -}); - -const mockMixin = { - methods: { - getProgress: jest.fn(), - }, -}; diff --git a/src/fmg-components/funnel-header/progress-bar/progress-bar.spec.js b/src/fmg-components/funnel-header/progress-bar/progress-bar.spec.js new file mode 100644 index 000000000..22c24ed4a --- /dev/null +++ b/src/fmg-components/funnel-header/progress-bar/progress-bar.spec.js @@ -0,0 +1,68 @@ +import { mount } from "@vue/test-utils"; +import ProgressBar from "./progress-bar.vue"; + +jest.mock("@/constants/progress-bar-mapper", () => ({ + getProgressBarPercentage: jest.fn(), +})); + +import { getProgressBarPercentage } from "@/constants/progress-bar-mapper"; + +describe("progress-bar.vue", () => { + beforeEach(() => { + jest.useFakeTimers(); + getProgressBarPercentage.mockReset(); + }); + + afterEach(() => { + jest.clearAllTimers(); + jest.useRealTimers(); + }); + + it("renders with correct initial progress", async () => { + getProgressBarPercentage.mockReturnValueOnce(30); + const wrapper = mount(ProgressBar, { + props: { page: "page1" }, + }); + // Immediately after mount, width should be lastProgress (0) + expect(wrapper.find(".progress-bar-inner").attributes("style")).toContain("width: 0%"); + // Advance timer to trigger animation + jest.runAllTimers(); + await wrapper.vm.$nextTick(); + expect(wrapper.find(".progress-bar-inner").attributes("style")).toContain("width: 30%"); + }); + + it("animates to new progress when page prop changes", async () => { + getProgressBarPercentage.mockReturnValueOnce(30); + const wrapper = mount(ProgressBar, { + props: { page: "page1" }, + }); + jest.runAllTimers(); + await wrapper.vm.$nextTick(); + expect(wrapper.find(".progress-bar-inner").attributes("style")).toContain("width: 30%"); + + getProgressBarPercentage.mockReturnValueOnce(60); + await wrapper.setProps({ page: "page2" }); + // Before timer, still old value + expect(wrapper.find(".progress-bar-inner").attributes("style")).toContain("width: 30%"); + jest.runAllTimers(); + await wrapper.vm.$nextTick(); + expect(wrapper.find(".progress-bar-inner").attributes("style")).toContain("width: 60%"); + }); + + it("clears timeout on unmount", async () => { + getProgressBarPercentage.mockReturnValueOnce(50); + const wrapper = mount(ProgressBar, { + props: { page: "page1" }, + }); + const clearTimeoutSpy = jest.spyOn(window, "clearTimeout"); + wrapper.unmount(); + expect(clearTimeoutSpy).toHaveBeenCalled(); + clearTimeoutSpy.mockRestore(); + }); +}); + +const mockMixin = { + methods: { + getProgress: jest.fn(), + }, +}; diff --git a/src/fmg-components/funnel-header/progress-bar/progress-bar.vue b/src/fmg-components/funnel-header/progress-bar/progress-bar.vue index ebce6e0de..f8b654e7f 100644 --- a/src/fmg-components/funnel-header/progress-bar/progress-bar.vue +++ b/src/fmg-components/funnel-header/progress-bar/progress-bar.vue @@ -1,6 +1,6 @@ @@ -8,20 +8,69 @@ import store from "@/store"; import { getProgressBarPercentage } from "@/constants/progress-bar-mapper"; +// Module-level variable to persist progress across component lifecycles +let lastProgress = 0; + export default { - name: "progressBar", - data() {}, + name: "progress-bar", + props: { + page: { + type: String, + required: true, + }, + }, + data() { + return { + displayedProgress: lastProgress, + timeoutId: null, + }; + }, + watch: { + page: { + immediate: true, + handler(newPage) { + const target = getProgressBarPercentage(newPage); + if (this.timeoutId) clearTimeout(this.timeoutId); + + // Animate from current value to new value + this.timeoutId = setTimeout(() => { + this.displayedProgress = target; + lastProgress = target; // Update the module-level variable + }, 150); + }, + }, + }, + beforeUnmount() { + if (this.timeoutId) clearTimeout(this.timeoutId); + }, computed: { - progress() { - return getProgressBarPercentage(this.pageName); + progressStyle() { + return { + width: this.displayedProgress + "%", + }; }, }, };