Repository: shopware/frontends
Pull request #1131 (2024-07-18 10:52:49)

feat(composables): add 100% coverage
### Description

<!-- Describe the changes you did and which issue you're closing -->

<!-- example: closes #007 -->

### Type of change

<!-- Comment out the type of change your PR is making -->

<!-- Bug fix (non-breaking change that fixes an issue) -->
<!-- New feature (non-breaking change which adds functionality) -->
<!-- Breaking change (fix or feature that would cause existing functionality to not work as expected) -->

### ToDo's

<!-- Add the todo's that need to be done before merge -->
<!-- Remember to run `pnpm run changeset` and describe your change (plus potential migration guide/important notes) to your pull request. -->

<!-- - [ ] Changeset file provided [read more](https://github.com/shopware/frontends/blob/main/CONTRIBUTION.md#changelog-preparation) -->
<!-- - [ ] Documentation added/updated -->
<!-- - [ ] Unit-Tests added/updated -->
<!-- - [ ] E2E-Tests added/updated -->
<!-- - [ ] Related Issue updated -->

### Screenshots (if applicable)

<!-- Please attach any relevant screenshots or images to help explain your changes. -->

### Additional context

<!-- Add any other context about the pull request here. -->


vercel[bot]: [vc]: #MLp7TmdZbpMDcPZvx4GbAqxdXLGhFW6bW00epqkLN5k=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJzaG9wd2FyZS1mcm9udGVuZHMtZG9jcyIsInJvb3REaXJlY3RvcnkiOiJhcHBzL2RvY3MvIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3Nob3B3YXJlLWZyb250ZW5kcy9zaG9wd2FyZS1mcm9udGVuZHMtZG9jcy81ZjRxVEVQVkJnNURMWWFHYmtwMXRwcGg0eEZYIiwicHJldmlld1VybCI6InNob3B3YXJlLWZyb250ZW5kcy1kb2NzLWdpdC1mZWF0LWNvbXAtY2QyOTNlLXNob3B3YXJlLWZyb250ZW5kcy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IlBFTkRJTkcifSx7Im5hbWUiOiJmcm9udGVuZHMtZGVtbyIsInJvb3REaXJlY3RvcnkiOiJ0ZW1wbGF0ZXMvdnVlLWRlbW8tc3RvcmUiLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vc2hvcHdhcmUtZnJvbnRlbmRzL2Zyb250ZW5kcy1kZW1vL0VUSm1BcEFuN2hRZjhZSjVaVWFXaTNTdURtUUMiLCJwcmV2aWV3VXJsIjoiZnJvbnRlbmRzLWRlbW8tZ2l0LWZlYXQtY29tcG9zYWJsZS10ZXN0cy1paS1zaG9wd2FyZS1mcm9udGVuZHMudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJQRU5ESU5HIn1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Updated (UTC) |
| :--- | :----- | :------ | :------ |
| **frontends-demo** | 🔄 Building ([Inspect](https://vercel.com/shopware-frontends/frontends-demo/ETJmApAn7hQf8YJ5ZUaWi3SuDmQC)) | [Visit Preview](https://frontends-demo-git-feat-composable-tests-ii-shopware-frontends.vercel.app) | Jul 18, 2024 10:52am |
| **shopware-frontends-docs** | 🔄 Building ([Inspect](https://vercel.com/shopware-frontends/shopware-frontends-docs/5f4qTEPVBg5DLYaGbkp1tpph4xFX)) | [Visit Preview](https://shopware-frontends-docs-git-feat-comp-cd293e-shopware-frontends.vercel.app) | Jul 18, 2024 10:52am |



codspeed-hq[bot]: ## [CodSpeed Performance Report](https://codspeed.io/shopware/frontends/branches/feat/composable-tests-ii)

### Merging #1131 will **not alter performance**

<sub>Comparing <code>feat/composable-tests-ii</code> (0689926) with <code>main</code> (db7c93f)</sub>



### Summary

`✅ 9` untouched benchmarks






Patch:
diff --git a/.changeset/small-ears-grin.md b/.changeset/small-ears-grin.md
new file mode 100644
index 00000000..04eee718
--- /dev/null
+++ b/.changeset/small-ears-grin.md
@@ -0,0 +1,5 @@
+---
+"vue-demo-store": patch
+---
+
+Add data-testid for pagination
diff --git a/packages/cms-base/components/public/cms/element/CmsElementProductListing.vue b/packages/cms-base/components/public/cms/element/CmsElementProductListing.vue
index 77214a7a..10e22260 100644
--- a/packages/cms-base/components/public/cms/element/CmsElementProductListing.vue
+++ b/packages/cms-base/components/public/cms/element/CmsElementProductListing.vue
@@ -189,15 +189,22 @@ compareRouteQueryWithInitialListing();
             />
           </div>
           <div class="text-center place-self-center mt-2 lg:mt-0">
-            <div class="inline-block align-top text-center md:text-left">
-              <label for="limit" class="inline mr-4">{{
-                translations.listing.perPage
-              }}</label>
+            <div
+              class="inline-block align-top text-center md:text-left"
+              data-testid="listing-pagination-limit-box"
+            >
+              <label
+                for="limit"
+                class="inline mr-4"
+                data-testid="listing-pagination-limit-label"
+                >{{ translations.listing.perPage }}</label
+              >
               <select
                 id="limit"
                 v-model="limit"
                 name="limitchoices"
                 class="inline appearance-none bg-white border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline"
+                data-testid="listing-pagination-limit-select"
                 @change="changeLimit"
               >
                 <option :value="1">1 {{ translations.listing.product }}</option>
diff --git a/packages/composables/src/index.ts b/packages/composables/src/index.ts
index 954b84c7..a07815b3 100644
--- a/packages/composables/src/index.ts
+++ b/packages/composables/src/index.ts
@@ -90,6 +90,7 @@ export function resolveCmsComponent(
       componentNameToResolve,
       resolvedComponent: undefined,
       resolved: false,
+      isResolved: false,
       error: (e as Error).message,
     };
   }
diff --git a/packages/composables/src/mocks/Order.ts b/packages/composables/src/mocks/Order.ts
index 09806fbb..66941438 100644
--- a/packages/composables/src/mocks/Order.ts
+++ b/packages/composables/src/mocks/Order.ts
@@ -1144,6 +1144,7 @@ const Order = {
   },
   paymentChangeable: {
     "018c6832f1a1731db276839b3f251fc2": true,
+    "123-test": true,
   },
 };
 
diff --git a/packages/composables/src/useListing.ts b/packages/composables/src/useListing.ts
index 4814f676..ad2cac14 100644
--- a/packages/composables/src/useListing.ts
+++ b/packages/composables/src/useListing.ts
@@ -73,7 +73,7 @@ export type UseListingReturn = {
    * Loads more (next page) elements to the listing
    */
   loadMore(
-    criteria: operations["searchPage post /search"]["body"],
+    criteria?: operations["searchPage post /search"]["body"],
   ): Promise<void>;
   /**
    * Listing that is currently set
diff --git a/packages/composables/src/useNotifications.ts b/packages/composables/src/useNotifications.ts
index d8694b09..ebb16269 100644
--- a/packages/composables/src/useNotifications.ts
+++ b/packages/composables/src/useNotifications.ts
@@ -84,7 +84,7 @@ export function useNotifications(): UseNotificationsReturn {
 
   async function pushNotification(
     message: string,
-    options: NotificationOptions,
+    options: Required<Pick<NotificationOptions, "type">> & NotificationOptions,
   ) {
     const timeout = options.timeout || 2500;
     const persistent = !!options.persistent;
@@ -93,7 +93,7 @@ export function useNotifications(): UseNotificationsReturn {
     const messageId = geterateId();
     _notifications.value.push({
       id: messageId,
-      type: options.type || "info",
+      type: options.type,
       message,
     });
     if (!persistent) {
diff --git a/packages/composables/src/useProduct.ts b/packages/composables/src/useProduct.ts
index f123a946..759c9380 100644
--- a/packages/composables/src/useProduct.ts
+++ b/packages/composables/src/useProduct.ts
@@ -35,6 +35,7 @@ export function useProduct(
     // TODO link docs with composables context usage
     throw new ContextError("Product");
   }
+
   const _configurator = useContext("configurator", {
     context: product && configurator,
   });
diff --git a/packages/composables/src/useProductAssociations.spec.ts b/packages/composables/src/useProductAssociations.spec.ts
index 6c430051..566ce195 100644
--- a/packages/composables/src/useProductAssociations.spec.ts
+++ b/packages/composables/src/useProductAssociations.spec.ts
@@ -1,6 +1,6 @@
 import { describe, expect, it, vi } from "vitest";
 import { useProductAssociations } from "./useProductAssociations";
-import { computed } from "vue";
+import { computed, ref } from "vue";
 import mockedProduct from "./mocks/Product";
 import mockedCrossSelling from "./mocks/CrossSellingResponse";
 import { useSetup } from "./_test";
@@ -88,4 +88,18 @@ describe("useProductAssociations", () => {
 
     expect(vm.productAssociations).toStrictEqual(mockedCrossSelling);
   });
+
+  it("init without product should throw an error", () => {
+    expect(() =>
+      useSetup(() =>
+        useProductAssociations(
+          // @ts-expect-error if API returns null we want to be prepared for it
+          ref(null),
+          {
+            associationContext: "cross-selling",
+          },
+        ),
+      ),
+    ).toThrowError("[useProductAssociations]: Product is not provided.");
+  });
 });
diff --git a/packages/composables/src/useProductConfigurator.spec.ts b/packages/composables/src/useProductConfigurator.spec.ts
index 27f82b05..97fc9bd6 100644
--- a/packages/composables/src/useProductConfigurator.spec.ts
+++ b/packages/composables/src/useProductConfigurator.spec.ts
@@ -4,16 +4,15 @@ import { ref } from "vue";
 import mockedProduct from "./mocks/Product";
 import mockedConfigurator from "./mocks/Configurator";
 import { useSetup } from "./_test";
+import { useProduct } from "./useProduct";
+
+vi.mock("./useProduct.ts");
 
 describe("useProductConfigurator", () => {
-  vi.mock("./useProduct.ts", () => ({
-    useProduct() {
-      return {
-        configurator: ref(mockedConfigurator),
-        product: ref(mockedProduct),
-      };
-    },
-  }));
+  vi.mocked(useProduct).mockReturnValue({
+    configurator: ref(mockedConfigurator),
+    product: ref(mockedProduct),
+  } as unknown as ReturnType<typeof useProduct>);
 
   it("handleChange callback function was called", () => {
     const { vm } = useSetup(useProductConfigurator);
@@ -55,4 +54,25 @@ describe("useProductConfigurator", () => {
       "PROBLEM",
     );
   });
+
+  it("product - with options", () => {
+    vi.mocked(useProduct).mockReturnValue({
+      configurator: ref(null),
+      product: ref({
+        options: [
+          {
+            id: "cc02c6cf39ad43d5856a25f8928490bf",
+          },
+          {
+            id: "aa02c6cf39ad43d5856a25f8928490bf",
+          },
+        ],
+      }),
+    } as unknown as ReturnType<typeof useProduct>);
+
+    const { vm } = useSetup(useProductConfigurator);
+
+    expect(vm.isLoadingOptions).toBe(true);
+    expect(vm.getOptionGroups).toStrictEqual([]);
+  });
 });
diff --git a/packages/composables/src/useProductPrice.ts b/packages/composables/src/useProductPrice.ts
index f71964ac..293a9ff6 100644
--- a/packages/composables/src/useProductPrice.ts
+++ b/packages/composables/src/useProductPrice.ts
@@ -71,37 +71,27 @@ export function useProductPrice(
     Schemas["CalculatedPrice"]["referencePrice"] | undefined
   > = computed(() => _real?.value?.referencePrice);
 
-  const _displayParent: ComputedRef<boolean> = computed(
-    () =>
-      !!product.value?.variantListingConfig?.displayParent &&
-      product.value?.parentId === null,
-  );
-
-  const displayFrom: ComputedRef<boolean> = computed(
-    () =>
-      (product.value?.calculatedPrices?.length ?? 0) > 1 ||
-      !!(_displayParent.value && displayFromVariants.value),
-  );
+  const displayFrom: ComputedRef<boolean> = computed(() => {
+    return (product.value?.calculatedPrices?.length ?? 0) > 1;
+  });
 
   const displayFromVariants: ComputedRef<number | false | undefined> = computed(
-    () =>
-      !!product.value?.parentId &&
-      product.value?.calculatedCheapestPrice?.hasRange &&
-      _real?.value?.unitPrice !== _cheapest?.value?.unitPrice &&
-      _cheapest?.value?.unitPrice,
+    () => {
+      return (
+        !!product.value?.parentId &&
+        product.value?.calculatedCheapestPrice?.hasRange &&
+        _real?.value?.unitPrice !== _cheapest?.value?.unitPrice &&
+        _cheapest?.value?.unitPrice
+      );
+    },
   );
 
   const _price: ComputedRef<Schemas["CalculatedPrice"] | undefined> = computed(
     () => {
       if (displayFrom.value && getProductTierPrices(product.value).length > 1) {
-        const lowest = product.value?.calculatedPrices?.reduce(
-          (previous, current) => {
-            return current.unitPrice < previous.unitPrice ? current : previous;
-          },
-        );
-        return (
-          lowest || (_cheapest.value as unknown as Schemas["CalculatedPrice"])
-        );
+        return product.value?.calculatedPrices?.reduce((previous, current) => {
+          return current.unitPrice < previous.unitPrice ? current : previous;
+        });
       }
       return _real.value;
     },
@@ -117,9 +107,9 @@ export function useProductPrice(
     () => _price.value,
   );
 
-  const isListPrice: ComputedRef<boolean> = computed(
-    () => !!_price.value?.listPrice?.percentage,
-  );
+  const isListPrice: ComputedRef<boolean> = computed(() => {
+    return !!_price.value?.listPrice?.percentage;
+  });
 
   const regulationPrice: ComputedRef<number | undefined> = computed(
     () => product.value?.calculatedPrice?.regulationPrice?.price,
diff --git a/templates/vue-demo-store/components/account/AccountOrderDetails.vue b/templates/vue-demo-store/components/account/AccountOrderDetails.vue
index 734bda88..14fa12ab 100644
--- a/templates/vue-demo-store/components/account/AccountOrderDetails.vue
+++ b/templates/vue-demo-store/components/account/AccountOrderDetails.vue
@@ -158,7 +158,12 @@ const handleReorder = async () => {
       :line-item="lineItem"
     />
     <AccountOrderDownloads v-if="hasDocuments" :documents="documents" />
-    <button class="mt-10 p-3" :disabled="addingProducts" @click="handleReorder">
+    <button
+      class="mt-10 p-3"
+      data-testid="order-repeat-button"
+      :disabled="addingProducts"
+      @click="handleReorder"
+    >
       {{ $t("account.order.repeatOrder") }}
     </button>
   </div>
diff --git a/templates/vue-demo-store/pages/account/order.vue b/templates/vue-demo-store/pages/account/order.vue
index b70d7760..8be1d9e2 100644
--- a/templates/vue-demo-store/pages/account/order.vue
+++ b/templates/vue-demo-store/pages/account/order.vue
@@ -118,15 +118,22 @@ await useAsyncData("getOrders", () => {
         />
       </div>
       <div class="text-center place-self-center mt-2 lg:mt-0">
-        <div class="inline-block align-top text-center md:text-left">
-          <label for="limit" class="inline mr-4">{{
-            translations.account.perPage
-          }}</label>
+        <div
+          class="inline-block align-top text-center md:text-left"
+          data-testid="order-pagination-limit-box"
+        >
+          <label
+            for="limit"
+            class="inline mr-4"
+            data-testid="order-pagination-limit-label"
+            >{{ translations.account.perPage }}</label
+          >
           <select
             id="limit"
             v-model="limit"
             name="limitchoices"
             class="inline appearance-none bg-white border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline"
+            data-testid="order-pagination-limit-select"
             @change="changeLimit"
           >
             <option :value="1">1 {{ translations.account.order.order }}</option>
diff --git a/templates/vue-demo-store/pages/wishlist.vue b/templates/vue-demo-store/pages/wishlist.vue
index 64f925ed..5d9cd1c5 100644
--- a/templates/vue-demo-store/pages/wishlist.vue
+++ b/templates/vue-demo-store/pages/wishlist.vue
@@ -219,15 +219,22 @@ onMounted(() => {
       />
     </div>
     <div class="text-center place-self-center mt-2 lg:mt-0">
-      <div class="inline-block align-top text-center md:text-left">
-        <label for="limit" class="inline mr-4">{{
-          translations.listing.perPage
-        }}</label>
+      <div
+        class="inline-block align-top text-center md:text-left"
+        data-testid="wishlist-pagination-limit-box"
+      >
+        <label
+          for="limit"
+          class="inline mr-4"
+          data-testid="wishlist-pagination-limit-label"
+          >{{ translations.listing.perPage }}</label
+        >
         <select
           id="limit"
           v-model="limit"
           name="limitchoices"
           class="inline appearance-none bg-white border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline"
+          data-testid="wishlist-pagination-limit-select"
           @change="changeLimit"
         >
           <option :value="1">1 {{ translations.listing.product }}</option>


Test patch:
diff --git a/packages/composables/src/_test/useSetup.ts b/packages/composables/src/_test/useSetup.ts
index 1261d90b..db216b02 100644
--- a/packages/composables/src/_test/useSetup.ts
+++ b/packages/composables/src/_test/useSetup.ts
@@ -8,6 +8,7 @@ type Injections = {
   apiClient: {
     invoke: ReturnType<typeof vi.fn>;
   };
+  swNotifications?: unknown;
 };
 
 export function useSetup<V>(setup: () => V, customMocks?: Partial<Injections>) {
@@ -16,7 +17,7 @@ export function useSetup<V>(setup: () => V, customMocks?: Partial<Injections>) {
     apiClient: { invoke: customMocks?.apiClient?.invoke ?? vi.fn() },
   };
 
-  const compoment = defineComponent({
+  const component = defineComponent({
     setup,
     render() {
       return h("div", []);
@@ -28,7 +29,7 @@ export function useSetup<V>(setup: () => V, customMocks?: Partial<Injections>) {
     customMocks || {},
   ) as Injections;
 
-  const wrapper = shallowMount(compoment, {
+  const wrapper = shallowMount(component, {
     global: {
       provide: injections,
     },
diff --git a/packages/composables/src/index.test.ts b/packages/composables/src/index.test.ts
index 6f2336d5..8122f0f4 100644
--- a/packages/composables/src/index.test.ts
+++ b/packages/composables/src/index.test.ts
@@ -1,9 +1,12 @@
-import { describe, expect, it } from "vitest";
+import { describe, expect, it, vi } from "vitest";
 import { resolveCmsComponent, getDefaultApiParams } from "./index";
 import CmsPage from "./mocks/CmsPage";
 import type { Schemas } from "#shopware";
+import * as vue from "vue";
 
+vi.mock("vue");
 describe("resolveCmsComponent", () => {
+  vi.spyOn(vue, "resolveComponent").mockImplementation((element) => element);
   it("should resolve a cms component", () => {
     const result = resolveCmsComponent(
       CmsPage.cmsPage.sections[0].blocks[0] as unknown as Schemas["CmsBlock"],
@@ -19,6 +22,7 @@ describe("resolveCmsComponent", () => {
   });
 
   it("cms section component", () => {
+    vi.spyOn(vue, "resolveComponent").mockImplementation(() => ({}));
     const result = resolveCmsComponent({
       apiAlias: "cms_section",
     } as unknown as Schemas["CmsBlock"]);
@@ -32,4 +36,15 @@ describe("resolveCmsComponent", () => {
 
     expect(result.isResolved).toBe(true);
   });
+
+  it("component should not be resolved because of the error", () => {
+    vi.spyOn(vue, "resolveComponent").mockImplementation(() => {
+      throw new Error("error");
+    });
+    const result = resolveCmsComponent({
+      apiAlias: "cms_custom",
+    } as unknown as Schemas["CmsBlock"]);
+
+    expect(result.isResolved).toBe(false);
+  });
 });
diff --git a/packages/composables/src/useCart.test.ts b/packages/composables/src/useCart.test.ts
index 4e4793bd..3adf8164 100644
--- a/packages/composables/src/useCart.test.ts
+++ b/packages/composables/src/useCart.test.ts
@@ -39,6 +39,9 @@ describe("useCart", () => {
   });
 
   it("add single product", async () => {
+    expect(vm.cartItems).toEqual([]);
+    expect(vm.totalPrice).toBe(0);
+    expect(vm.subtotal).toBe(0);
     await vm.addProduct({
       id: itemsMock.items[0].referencedId as string,
       quantity: itemsMock.items[0].quantity,
@@ -58,6 +61,25 @@ describe("useCart", () => {
         },
       }),
     );
+
+    await vm.addProduct({
+      id: itemsMock.items[0].referencedId as string,
+    });
+
+    expect(injections.apiClient.invoke).toHaveBeenCalledWith(
+      expect.stringContaining("addLineItem"),
+      expect.objectContaining({
+        body: {
+          items: [
+            {
+              type: itemsMock.items[0].type,
+              id: itemsMock.items[0].referencedId,
+              quantity: 0,
+            },
+          ],
+        },
+      }),
+    );
   });
 
   it("refresh the cart", async () => {
@@ -71,6 +93,11 @@ describe("useCart", () => {
     expect(await vm.refreshCart(Cart as unknown as Schemas["Cart"])).toEqual(
       Cart,
     );
+
+    expect(vm.shippingTotal).toBe(0);
+    expect(vm.subtotal).toBe(16.11);
+    expect(vm.isVirtualCart).toBe(false);
+    expect(vm.totalPrice).toBe(16.11);
   });
 
   it("add set of products", async () => {
@@ -116,4 +143,76 @@ describe("useCart", () => {
       }),
     );
   });
+
+  it("submitPromotionCode", async () => {
+    injections.apiClient.invoke.mockResolvedValue({
+      data: {
+        lineItems: [
+          {
+            type: "promotion",
+            payload: {
+              code: "3a64e872ca404522a2c5d43ebc751e6b",
+            },
+          },
+          {
+            type: "product",
+            good: true,
+            quantity: 1,
+            id: "e05e9340aff4484f9009646dfd572df9",
+          },
+        ],
+      },
+    });
+
+    await vm.addPromotionCode("PROMO_CODE");
+    expect(injections.apiClient.invoke).toHaveBeenCalledWith(
+      expect.stringContaining("addLineItem"),
+      expect.objectContaining({
+        body: {
+          items: [
+            {
+              referencedId: "PROMO_CODE",
+              type: "promotion",
+            },
+          ],
+        },
+      }),
+    );
+
+    expect(vm.appliedPromotionCodes).toEqual([
+      {
+        type: "promotion",
+        payload: {
+          code: "3a64e872ca404522a2c5d43ebc751e6b",
+        },
+      },
+    ]);
+    expect(vm.count).toBe(1);
+    expect(vm.totalPrice).toBe(0);
+  });
+
+  it("handle api cart error", async () => {
+    injections.apiClient.invoke.mockResolvedValue({
+      data: {
+        errors: [
+          {
+            status: 400,
+            code: "CHECKOUT__CART_ITEM_NOT_FOUND",
+            detail: "Line item not found",
+          },
+        ],
+      },
+    });
+    await vm.refreshCart();
+    await vm.addProducts(itemsMock.items);
+    expect(vm.consumeCartErrors()).toEqual({
+      "0": {
+        status: 400,
+        code: "CHECKOUT__CART_ITEM_NOT_FOUND",
+        detail: "Line item not found",
+      },
+    });
+
+    expect(vm.consumeCartErrors()).toEqual(null);
+  });
 });
diff --git a/packages/composables/src/useCartItem.test.ts b/packages/composables/src/useCartItem.test.ts
index cc955983..7184fc54 100644
--- a/packages/composables/src/useCartItem.test.ts
+++ b/packages/composables/src/useCartItem.test.ts
@@ -123,4 +123,46 @@ describe("useCartItem", () => {
       id: lineItem.id,
     });
   });
+
+  it("itemSpecialPrice", () => {
+    const { vm } = useSetup(() =>
+      useCartItem(
+        ref(
+          Object.assign(
+            { ...lineItem },
+            {
+              price: {
+                listPrice: {
+                  price: 10,
+                },
+                unitPrice: 5,
+              },
+            },
+          ),
+        ) as unknown as Ref<Schemas["LineItem"]>,
+      ),
+    );
+    expect(vm.itemSpecialPrice).toBe(5);
+  });
+  it("itemRegularPrice from listPrice", () => {
+    const { vm } = useSetup(() =>
+      useCartItem(
+        ref({
+          price: {
+            listPrice: {
+              price: 10,
+            },
+          },
+        }) as unknown as Ref<Schemas["LineItem"]>,
+      ),
+    );
+    expect(vm.itemRegularPrice).toBe(10);
+  });
+
+  it("should throw an error if cartItem is not provided", () => {
+    expect(() =>
+      // @ts-expect-error we deliberately pass null to invoke error
+      useCartItem(null),
+    ).toThrow("[useCartItem] mandatory cartItem argument is missing.");
+  });
 });
diff --git a/packages/composables/src/useCustomerOrders.test.ts b/packages/composables/src/useCustomerOrders.test.ts
index cfa108ad..e6d29f60 100644
--- a/packages/composables/src/useCustomerOrders.test.ts
+++ b/packages/composables/src/useCustomerOrders.test.ts
@@ -3,7 +3,7 @@ import { useCustomerOrders } from "./useCustomerOrders";
 import { useSetup } from "./_test";
 
 describe("useCustomerOrders", () => {
-  it("changeLanguage", async () => {
+  it("should inovke load orders", async () => {
     const { vm, injections } = useSetup(useCustomerOrders);
     injections.apiClient.invoke.mockResolvedValue({
       data: { orders: { elements: [] } },
@@ -13,9 +13,10 @@ describe("useCustomerOrders", () => {
       expect.stringContaining("readOrder"),
       expect.objectContaining({}),
     );
+    expect(vm.totalPages).toBe(0);
   });
 
-  it("changeCurrentPage", async () => {
+  it("should invoke change page", async () => {
     const { vm, injections } = useSetup(useCustomerOrders);
     injections.apiClient.invoke.mockResolvedValue({
       data: { orders: { elements: [] } },
diff --git a/packages/composables/src/useInternationalization.test.ts b/packages/composables/src/useInternationalization.test.ts
index 7f592e21..345adfc0 100644
--- a/packages/composables/src/useInternationalization.test.ts
+++ b/packages/composables/src/useInternationalization.test.ts
@@ -1,6 +1,8 @@
 import { describe, expect, it, vi } from "vitest";
 import { useInternationalization } from "./useInternationalization";
+import type { RouteObject } from "./useInternationalization";
 import { useSetup } from "./_test";
+import type { Schemas } from "#shopware";
 
 describe("useInternationalization", () => {
   it("should return storefrontUrl", async () => {
@@ -21,7 +23,7 @@ describe("useInternationalization", () => {
     expect(vm.getStorefrontUrl()).toBe("http://localhost:3000");
   });
 
-  it("getAvailableLanguages", async () => {
+  it("should invoke getting available languages", async () => {
     const { vm, injections } = useSetup(useInternationalization);
     injections.apiClient.invoke.mockResolvedValue({ data: {} });
     vm.getAvailableLanguages();
@@ -30,7 +32,7 @@ describe("useInternationalization", () => {
     );
   });
 
-  it("changeLanguage", async () => {
+  it("should invoke change language", async () => {
     const { vm, injections } = useSetup(useInternationalization);
     injections.apiClient.invoke.mockResolvedValue({ data: {} });
     vm.changeLanguage("test-id");
@@ -39,4 +41,115 @@ describe("useInternationalization", () => {
       expect.objectContaining({ body: { languageId: "test-id" } }),
     );
   });
+
+  it("getLanguageCodeFromId", async () => {
+    const { vm } = useSetup(useInternationalization);
+    vm.languages = [
+      { id: "test-id", translationCode: { code: "test-code" } },
+    ] as Schemas["Language"][];
+    expect(vm.getLanguageCodeFromId("test-id")).toBe("test-code");
+  });
+
+  it("getLanguageCodeFromId -  no translationCode", async () => {
+    const { vm } = useSetup(useInternationalization);
+    vm.languages = [{ id: "test-id" }] as Schemas["Language"][];
+    expect(vm.getLanguageCodeFromId("test-id")).toBe("");
+  });
+
+  it("getLanguageCodeFromId - no languages", async () => {
+    const { vm } = useSetup(useInternationalization);
+    expect(() => vm.getLanguageCodeFromId("test-id")).toThrowError();
+  });
+
+  it("getLanguageIdFromCode", async () => {
+    const { vm } = useSetup(useInternationalization);
+    vm.languages = [
+      { id: "test-id", translationCode: { code: "test-code" } },
+    ] as Schemas["Language"][];
+    expect(vm.getLanguageIdFromCode("test-code")).toBe("test-id");
+  });
+
+  it("getLanguageIdFromCode - no id", async () => {
+    const { vm } = useSetup(useInternationalization);
+    vm.languages = [
+      { translationCode: { code: "test-code" } },
+    ] as Schemas["Language"][];
+    expect(vm.getLanguageIdFromCode("test-code")).toBe("");
+  });
+
+  it("getLanguageIdFromCode - no languages", async () => {
+    const { vm } = useSetup(useInternationalization);
+    expect(() => vm.getLanguageIdFromCode("test-code")).toThrowError();
+  });
+
+  it("should return the storefront url with the devStorefrontUrl", async () => {
+    const url = "http://frontend.test";
+    const { vm } = useSetup(useInternationalization);
+
+    expect(vm.replaceToDevStorefront(url)).toBe(url);
+  });
+
+  it("formatLink", async () => {
+    const { vm } = useSetup(() =>
+      useInternationalization((element) => element),
+    );
+    expect(vm.formatLink("test")).toBe("test");
+  });
+
+  it("formatLink with path", async () => {
+    const { vm } = useSetup(() =>
+      useInternationalization((element) => element),
+    );
+    expect(vm.formatLink({ path: "test" })).toStrictEqual({ path: "test" });
+  });
+
+  it("formatLink custom structure", async () => {
+    const { vm } = useSetup(() =>
+      useInternationalization((element) => element),
+    );
+    expect(
+      vm.formatLink({ custom: "test" } as unknown as RouteObject),
+    ).toStrictEqual({
+      custom: "test",
+    });
+  });
+
+  it("replaceToDevStorefront with devStorefrontUrl", async () => {
+    const { vm } = useSetup(
+      () => useInternationalization((element) => element),
+      {
+        shopware: { devStorefrontUrl: "http://dev-storefront.test" },
+      },
+    );
+    expect(vm.getStorefrontUrl()).toBe("http://dev-storefront.test");
+    expect(vm.replaceToDevStorefront("http://localhost:3000/test")).toBe(
+      "http://dev-storefront.test/test",
+    );
+  });
+
+  it("getStorefrontUrl without devStorefrontUrl and window", async () => {
+    vi.spyOn(window, "location", "get").mockImplementation(
+      () => ({}) as Location,
+    );
+    const { vm } = useSetup(
+      () => useInternationalization((element) => element),
+      {
+        shopware: { devStorefrontUrl: null },
+      },
+    );
+
+    expect(vm.getStorefrontUrl()).toBe("");
+  });
+
+  it("pathResolver absolute path", async () => {
+    const { vm } = useSetup(() =>
+      useInternationalization((element) => element),
+    );
+    expect(vm.formatLink("http://www.test.test")).toBe("http://www.test.test");
+  });
+
+  it("pathResolver without resolver", async () => {
+    const { vm } = useSetup(() => useInternationalization());
+    expect(vm.formatLink("test")).toBe("test");
+  });
 });
diff --git a/packages/composables/src/useListing.test.ts b/packages/composables/src/useListing.test.ts
index 25e289a0..8bb87cce 100644
--- a/packages/composables/src/useListing.test.ts
+++ b/packages/composables/src/useListing.test.ts
@@ -1,11 +1,12 @@
 import { describe, expect, it } from "vitest";
-import { useListing } from "./useListing";
+import { useListing, useCategoryListing } from "./useListing";
 import { useSetup } from "./_test";
 import searchMock from "./mocks/Search";
 import ContextError from "./helpers/ContextError";
+import type { Schemas } from "#shopware";
 
 describe("useListing", () => {
-  it("invoke search", async () => {
+  it("should invoke search", async () => {
     const { vm, injections } = await useSetup(() =>
       useListing({
         listingType: "productSearchListing",
@@ -28,7 +29,7 @@ describe("useListing", () => {
     expect(vm.getLimit).toBe(10);
   });
 
-  it('invoke "readQuotes"', async () => {
+  it('should invoke "readQuotes"', async () => {
     const { vm, injections } = await useSetup(() =>
       useListing({
         listingType: "categoryListing",
@@ -55,7 +56,7 @@ describe("useListing", () => {
     );
   });
 
-  it('invoke "readQuotes" - errors', async () => {
+  it("should handle context error", async () => {
     let error: unknown | null = null;
     try {
       await useSetup(() =>
@@ -69,7 +70,7 @@ describe("useListing", () => {
     expect(error).toEqual(new ContextError("Category"));
   });
 
-  it("setCurrentFilters", async () => {
+  it("should set current filters", async () => {
     const { vm, injections } = await useSetup(() =>
       useListing({
         listingType: "categoryListing",
@@ -138,4 +139,112 @@ describe("useListing", () => {
       }),
     );
   });
+
+  it("useCategoryListing - error is thrown when listingContext was not provided in parent tree", async () => {
+    expect(() => useSetup(useCategoryListing)).toThrowError();
+  });
+
+  it("setInitialListing", async () => {
+    const { vm } = await useSetup(() =>
+      useListing({
+        listingType: "categoryListing",
+        categoryId: "1234",
+      }),
+    );
+
+    vm.setInitialListing({
+      apiAlias: "product_listing",
+    } as Schemas["ProductListingResult"]);
+
+    expect(vm.getInitialListing).toEqual({ apiAlias: "product_listing" });
+  });
+
+  it("initSearch", () => {
+    const { vm, injections } = useSetup(() =>
+      useListing({ listingType: "productSearchListing" }),
+    );
+    injections.apiClient.invoke.mockResolvedValue({ data: {} });
+    vm.initSearch({
+      search: "test",
+    });
+
+    expect(vm.getCurrentPage).toBe(1);
+    expect(vm.getTotalPagesCount).toBe(0);
+    expect(vm.getLimit).toBe(10);
+  });
+
+  it("loadMore", async () => {
+    const { vm, injections } = await useSetup(() =>
+      useListing({
+        listingType: "productSearchListing",
+      }),
+    );
+    injections.apiClient.invoke.mockResolvedValue({ data: { page: 1 } });
+    await vm.loadMore();
+
+    expect(injections.apiClient.invoke).toHaveBeenCalledWith(
+      expect.stringContaining("search"),
+      expect.objectContaining({
+        body: {
+          p: 2,
+        },
+        headers: {
+          "sw-include-seo-urls": true,
+        },
+      }),
+    );
+  });
+
+  it("changeCurrentSortingOrder", async () => {
+    const { vm, injections } = await useSetup(() =>
+      useListing({
+        listingType: "productSearchListing",
+      }),
+    );
+    injections.apiClient.invoke.mockResolvedValue({ data: { page: 1 } });
+    vm.changeCurrentSortingOrder("test");
+
+    expect(injections.apiClient.invoke).toHaveBeenCalledWith(
+      expect.stringContaining("search"),
+      expect.objectContaining({
+        body: {
+          order: "test",
+        },
+        headers: {
+          "sw-include-seo-urls": true,
+        },
+      }),
+    );
+  });
+
+  it("changeCurrentPage", async () => {
+    const { vm, injections } = await useSetup(() =>
+      useListing({
+        listingType: "productSearchListing",
+      }),
+    );
+    injections.apiClient.invoke.mockResolvedValue({ data: { page: 1 } });
+    vm.changeCurrentPage(2);
+
+    expect(injections.apiClient.invoke).toHaveBeenCalledWith(
+      expect.stringContaining("search"),
+      expect.objectContaining({
+        body: {
+          page: 2,
+        },
+        headers: {
+          "sw-include-seo-urls": true,
+        },
+      }),
+    );
+  });
+
+  it("getAvailableFilters", async () => {
+    const { vm } = await useSetup(() =>
+      useListing({
+        listingType: "productSearchListing",
+      }),
+    );
+    expect(vm.getAvailableFilters).toEqual([]);
+  });
 });
diff --git a/packages/composables/src/useLocalWishlist.test.ts b/packages/composables/src/useLocalWishlist.test.ts
index 4be5381e..f026c36a 100644
--- a/packages/composables/src/useLocalWishlist.test.ts
+++ b/packages/composables/src/useLocalWishlist.test.ts
@@ -4,7 +4,7 @@ import { useLocalWishlist } from "./useLocalWishlist";
 
 describe("useLocalWishlist", () => {
   describe("methods", () => {
-    describe("addToWishlist", () => {
+    describe("should add product to the wishlist", () => {
       it("wishlist add product", () => {
         const { vm } = useSetup(useLocalWishlist);
         vm.addToWishlist("some-id");
@@ -13,23 +13,15 @@ describe("useLocalWishlist", () => {
     });
 
     describe("removeFromWishlist", () => {
-      it("wishlist add product", () => {
+      it("should remove product from the wishlist", () => {
         const { vm } = useSetup(useLocalWishlist);
         vm.removeFromWishlist("some-id");
         expect(vm.items.length).toBe(0);
       });
     });
 
-    describe("removeFromWishlist", () => {
-      it("wishlist remove product", () => {
-        const { vm } = useSetup(useLocalWishlist);
-        vm.removeFromWishlist("some-id");
-        expect(vm.count).toBe(0);
-      });
-    });
-
     describe("clearWishlist", () => {
-      it("clearWishlist", () => {
+      it("should clear the wishlist", () => {
         const { vm } = useSetup(useLocalWishlist);
         vm.addToWishlist("some-id");
         expect(vm.count).toBe(1);
@@ -39,18 +31,24 @@ describe("useLocalWishlist", () => {
     });
 
     describe("getWishlistProducts", () => {
-      it("getWishlistProducts", () => {
+      it("should get the wishlist products", () => {
         const { vm } = useSetup(useLocalWishlist);
         vm.clearWishlist();
         vm.getWishlistProducts();
         expect(vm.count).toBe(0);
       });
-      it("getWishlistProducts with items", () => {
+      it("should get the wishlist products after adding product", () => {
         const { vm } = useSetup(useLocalWishlist);
         vm.addToWishlist("some-id");
         vm.getWishlistProducts();
         expect(vm.count).toBe(1);
       });
+      it("getWishlistProducts with items localStorage", () => {
+        const { vm } = useSetup(useLocalWishlist);
+        localStorage.removeItem("sw-wishlist-items");
+        vm.getWishlistProducts();
+        expect(vm.count).toBe(1);
+      });
     });
   });
 });
diff --git a/packages/composables/src/useNavigationSearch.test.ts b/packages/composables/src/useNavigationSearch.test.ts
index 20c424e9..73e24f20 100644
--- a/packages/composables/src/useNavigationSearch.test.ts
+++ b/packages/composables/src/useNavigationSearch.test.ts
@@ -1,6 +1,15 @@
-import { describe, expect, it } from "vitest";
+import { describe, expect, it, vi } from "vitest";
 import { useNavigationSearch } from "./useNavigationSearch";
 import { useSetup } from "./_test";
+import { useSessionContext } from "./useSessionContext";
+import { ref } from "vue";
+
+vi.mock("./useSessionContext.ts");
+const sessionContext = ref();
+
+vi.mocked(useSessionContext).mockReturnValue({
+  sessionContext,
+} as unknown as ReturnType<typeof useSessionContext>);
 
 const mockedResponse = {
   translated: [],
@@ -85,4 +94,18 @@ describe("useNavigationSearch", () => {
     `);
     expect(injections.apiClient.invoke).not.toHaveBeenCalled();
   });
+
+  it("resolvePath with categoryId", async () => {
+    const { vm } = useSetup(useNavigationSearch);
+    sessionContext.value = {
+      salesChannel: {
+        navigationCategoryId: "categoryIdTest",
+      },
+    };
+
+    expect(await vm.resolvePath("/")).toStrictEqual({
+      foreignKey: "categoryIdTest",
+      routeName: "frontend.navigation.page",
+    });
+  });
 });
diff --git a/packages/composables/src/useNewsletter.test.ts b/packages/composables/src/useNewsletter.test.ts
index 5e94ef7f..bde1a1f8 100644
--- a/packages/composables/src/useNewsletter.test.ts
+++ b/packages/composables/src/useNewsletter.test.ts
@@ -33,4 +33,23 @@ describe("useNewsletter", () => {
       },
     );
   });
+
+  it("getNewsletterStatus", async () => {
+    const { vm, injections } = useSetup(useNewsletter);
+    injections.apiClient.invoke.mockResolvedValue({ data: {} });
+
+    await vm.getNewsletterStatus();
+
+    expect(injections.apiClient.invoke).toHaveBeenCalledWith(
+      expect.stringContaining("readNewsletterRecipient"),
+    );
+  });
+
+  it("isNewsletterSubscriber", async () => {
+    const { vm } = useSetup(useNewsletter);
+
+    expect(vm.isNewsletterSubscriber).toBe(false);
+    vm.newsletterStatus = "direct";
+    expect(vm.isNewsletterSubscriber).toBe(true);
+  });
 });
diff --git a/packages/composables/src/useNotifications.test.ts b/packages/composables/src/useNotifications.test.ts
index ab2828c0..d8b5f55e 100644
--- a/packages/composables/src/useNotifications.test.ts
+++ b/packages/composables/src/useNotifications.test.ts
@@ -1,6 +1,7 @@
 import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
 import { useNotifications } from "./useNotifications";
 import { useSetup } from "./_test";
+
 describe("useNotifications", () => {
   beforeEach(() => {
     vi.useFakeTimers();
@@ -10,7 +11,7 @@ describe("useNotifications", () => {
     vi.restoreAllMocks();
   });
 
-  it("notification flow", async () => {
+  it("should trigger sample notification flow", async () => {
     const { vm } = useSetup(useNotifications);
 
     vm.pushError("Error message");
@@ -32,4 +33,24 @@ describe("useNotifications", () => {
     vi.runAllTimers();
     expect(vm.notifications.length).toBe(0);
   });
+
+  it("injected empty swNotifications", () => {
+    const { vm } = useSetup(useNotifications, {
+      swNotifications: { value: null },
+    });
+
+    expect(vm.notifications).toEqual([]);
+    vm.removeOne(2332);
+    expect(vm.notifications).toEqual([]);
+    vm.pushSuccess("test");
+  });
+
+  it("injected empty swNotifications and push success", () => {
+    const { vm } = useSetup(useNotifications, {
+      swNotifications: { value: null },
+    });
+
+    vm.pushSuccess("test");
+    expect(vm.notifications.length).toBe(1);
+  });
 });
diff --git a/packages/composables/src/useOrderDetails.test.ts b/packages/composables/src/useOrderDetails.test.ts
index 060dd7c9..479c3d8f 100644
--- a/packages/composables/src/useOrderDetails.test.ts
+++ b/packages/composables/src/useOrderDetails.test.ts
@@ -28,7 +28,7 @@ describe("useOrderDetails", () => {
     );
   });
 
-  it("handlePayment", async () => {
+  it("should handle setting the order payment", async () => {
     const { vm, injections } = useSetup(() => useOrderDetails("123-test"));
     injections.apiClient.invoke.mockResolvedValue({ data: {} });
     await vm.handlePayment();
@@ -45,7 +45,7 @@ describe("useOrderDetails", () => {
     );
   });
 
-  it("cancel", async () => {
+  it("should cancel the order", async () => {
     const { vm, injections } = useSetup(() => useOrderDetails("123-test"));
     injections.apiClient.invoke.mockResolvedValue({ data: {} });
     await vm.cancel();
@@ -108,4 +108,27 @@ describe("useOrderDetails", () => {
       }),
     );
   });
+
+  it("getPaymentMethods", async () => {
+    const { vm, injections } = useSetup(() => useOrderDetails("123-test"));
+    injections.apiClient.invoke.mockResolvedValue({ data: {} });
+    await vm.getPaymentMethods();
+
+    expect(injections.apiClient.invoke).toHaveBeenCalledWith(
+      expect.stringContaining("readPaymentMethod"),
+      expect.objectContaining({
+        body: {
+          onlyAvailable: true,
+        },
+      }),
+    );
+  });
+
+  it("paymentChangeable", async () => {
+    const { vm, injections } = useSetup(() => useOrderDetails("123-test", {}));
+    injections.apiClient.invoke.mockResolvedValue({ data: Order });
+    expect(vm.paymentChangeable).toEqual(false);
+    await vm.loadOrderDetails();
+    expect(vm.paymentChangeable).toEqual(true);
+  });
 });
diff --git a/packages/composables/src/useOrderPayment.test.ts b/packages/composables/src/useOrderPayment.test.ts
index 9158f98f..86288c5c 100644
--- a/packages/composables/src/useOrderPayment.test.ts
+++ b/packages/composables/src/useOrderPayment.test.ts
@@ -7,7 +7,7 @@ import type { Schemas } from "#shopware";
 import Order from "./mocks/Order";
 
 describe("useOrderPayment", () => {
-  it("handlePayment", async () => {
+  it("should handle the order payment", async () => {
     const { vm, injections } = useSetup(() =>
       useOrderPayment(
         computed(() => Order.orders.elements[0]) as unknown as ComputedRef<
@@ -32,5 +32,49 @@ describe("useOrderPayment", () => {
     expect(vm.activeTransaction).toEqual(
       Order.orders.elements[0].transactions[0],
     );
+
+    vm.changePaymentMethod("test");
+
+    expect(injections.apiClient.invoke).toHaveBeenCalledWith(
+      expect.stringContaining("orderSetPayment"),
+      expect.objectContaining({
+        body: {
+          orderId: "018c6832f1a1731db276839b3f251fc2",
+          paymentMethodId: "test",
+        },
+      }),
+    );
+  });
+
+  it("should not invoke API calls for empty order", async () => {
+    const { vm } = useSetup(() =>
+      useOrderPayment(
+        computed(() => null) as unknown as ComputedRef<Schemas["Order"]>,
+      ),
+    );
+    expect(vm.changePaymentMethod("test")).resolves.toBeUndefined();
+    expect(vm.handlePayment()).resolves.toBeUndefined();
+  });
+
+  it("should be a asynchronous payment", () => {
+    const { vm } = useSetup(() =>
+      useOrderPayment(
+        computed(() =>
+          Object.assign(Order.orders.elements[0], {
+            transactions: [
+              {
+                paymentMethod: {
+                  active: true,
+                  asynchronous: true,
+                  afterOrderEnabled: true,
+                },
+              },
+            ],
+          }),
+        ) as unknown as ComputedRef<Schemas["Order"]>,
+      ),
+    );
+
+    expect(vm.isAsynchronous).toBe(true);
   });
 });
diff --git a/packages/composables/src/usePrice.test.ts b/packages/composables/src/usePrice.test.ts
index aca3c15f..a0f3a558 100644
--- a/packages/composables/src/usePrice.test.ts
+++ b/packages/composables/src/usePrice.test.ts
@@ -1,12 +1,53 @@
-import { describe, expect, it } from "vitest";
+import { describe, expect, it, vi, beforeEach } from "vitest";
+import { ref } from "vue";
 import { usePrice } from "./usePrice";
 import { useSetup } from "./_test";
+import { useSessionContext } from "./useSessionContext";
+const currentNavigator = global.navigator;
+
+vi.mock("./useSessionContext.ts");
+const sessionContext = ref();
+
+beforeEach(() => {
+  vi.clearAllMocks();
+  vi.mocked(useSessionContext).mockReturnValue({
+    sessionContext,
+  } as unknown as ReturnType<typeof useSessionContext>);
+});
 
 describe("usePrice", () => {
   it("should be defined", () => {
     expect(usePrice).toBeDefined();
   });
 
+  it("use default locale if locale is not provided", () => {
+    vi.spyOn(global, "navigator", "get").mockImplementation(
+      () => ({}) as Navigator,
+    );
+    const { vm } = useSetup(usePrice);
+    expect(vm.getFormattedPrice(2.55)).toMatchInlineSnapshot('"2.55"');
+    vm.update({
+      currencyCode: "USD",
+      localeCode: undefined,
+    });
+
+    expect(vm.getFormattedPrice(2.55)).toMatchInlineSnapshot('"$2.55"');
+  });
+
+  it("should use navigator language if locale is not provided", () => {
+    vi.spyOn(global, "navigator", "get").mockImplementation(
+      () => currentNavigator,
+    );
+    vi.spyOn(navigator, "language", "get").mockImplementation(() => "en-FR");
+    const { vm } = useSetup(usePrice);
+    vm.update({
+      currencyCode: "USD",
+      localeCode: undefined,
+    });
+
+    expect(vm.getFormattedPrice(2.55)).toMatchInlineSnapshot('"$2.55"');
+  });
+
   it("should init price object", () => {
     const { vm } = useSetup(() =>
       usePrice({
@@ -43,4 +84,15 @@ describe("usePrice", () => {
     const { vm } = useSetup(usePrice);
     expect(vm.getFormattedPrice(undefined)).toBe("");
   });
+
+  it("watch currency change", async () => {
+    const { vm } = useSetup(usePrice);
+    sessionContext.value = {
+      currency: {
+        isoCode: "PLN",
+      },
+    };
+    await vm.$nextTick();
+    expect(vm.currencyCode).toBe("PLN");
+  });
 });
diff --git a/packages/composables/src/useProduct.test.ts b/packages/composables/src/useProduct.test.ts
index a5349865..06248fe8 100644
--- a/packages/composables/src/useProduct.test.ts
+++ b/packages/composables/src/useProduct.test.ts
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
 import { useProduct } from "./useProduct";
 import mockedProduct from "./mocks/Product";
 import { useSetup } from "./_test";
+import { ref } from "vue";
 
 describe("useProduct", () => {
   it("should return product object", () => {
@@ -10,9 +11,26 @@ describe("useProduct", () => {
     expect(vm.product).toStrictEqual(mockedProduct);
   });
 
-  it("should return undefined configurator object", () => {
+  it("changeVariant - should merge the current product with the new variant data", () => {
     const { vm } = useSetup(() => useProduct(mockedProduct));
+    expect(vm.changeVariant()).toBeUndefined();
+  });
+
+  it("changeVariant - empty", () => {
+    const { vm } = useSetup(useProduct);
+
+    vm.changeVariant(Object.assign({ ...mockedProduct }, { id: "new-id" }));
+    expect(vm.product.id).toBe("new-id");
+  });
 
-    expect(vm.configurator).toBe(undefined);
+  it("init without product should throw an error", () => {
+    expect(() =>
+      useSetup(() =>
+        useProduct(
+          // @ts-expect-error if API returns null we want to be prepared for it
+          ref(null),
+        ),
+      ),
+    ).toThrowError("Product context is not provided");
   });
 });
diff --git a/packages/composables/src/useProductPrice.test.ts b/packages/composables/src/useProductPrice.test.ts
index 10be3128..9db6c3a6 100644
--- a/packages/composables/src/useProductPrice.test.ts
+++ b/packages/composables/src/useProductPrice.test.ts
@@ -1,4 +1,4 @@
-import { describe, expect, it } from "vitest";
+import { describe, expect, it, vi, beforeEach } from "vitest";
 import { useProductPrice } from "./useProductPrice";
 import { ref } from "vue";
 import mockedProduct from "./mocks/Product";
@@ -7,6 +7,9 @@ import productTierPrices from "./mocks/ProductTierPrices";
 import type { Schemas } from "#shopware";
 
 describe("useProductPrice", () => {
+  beforeEach(() => {
+    vi.resetAllMocks();
+  });
   it("product price are displayed - standard product", () => {
     const { vm } = useSetup(() => useProductPrice(ref(mockedProduct)));
 
@@ -40,21 +43,17 @@ describe("useProductPrice", () => {
     expect(vm.displayFromVariants).toBe(20);
   });
 
-  it("displayFrom", () => {
+  it("displayFrom should be false if displayParent is true", () => {
     const { vm } = useSetup(() =>
       useProductPrice(
         ref(
           Object.assign(
             { ...productTierPrices.product },
             {
-              parentId: null,
+              calculatedPrices: null,
               variantListingConfig: {
                 displayParent: true,
               },
-              calculatedPrices: undefined,
-              listPrice: {
-                percentage: 100,
-              },
             },
           ) as unknown as Schemas["Product"],
         ),
@@ -63,4 +62,65 @@ describe("useProductPrice", () => {
     expect(vm.displayFrom).toBe(false);
     expect(vm.isListPrice).toBe(false);
   });
+
+  it("isListPrice should be false with more than one calculatedPrices value", () => {
+    const { vm } = useSetup(() =>
+      useProductPrice(
+        ref(
+          Object.assign(
+            { ...productTierPrices.product },
+            {
+              listPrice: 80,
+              calculatedPrices: [
+                {
+                  unitPrice: 10,
+                },
+                {
+                  unitPrice: 20,
+                },
+              ],
+            },
+          ) as unknown as Schemas["Product"],
+        ),
+      ),
+    );
+    expect(vm.displayFrom).toBe(true);
+    expect(vm.isListPrice).toBe(false);
+  });
+
+  it("isListPrice should be true with one calculatedPrices value", () => {
+    const { vm } = useSetup(() =>
+      useProductPrice(
+        ref({
+          calculatedPrices: [
+            {
+              listPrice: {
+                percentage: 10,
+              },
+            },
+          ],
+        } as unknown as Schemas["Product"]),
+      ),
+    );
+    expect(vm.displayFrom).toBe(false);
+    expect(vm.isListPrice).toBe(true);
+  });
+
+  it("displayFrom - second variant", async () => {
+    const { vm } = useSetup(() =>
+      useProductPrice(
+        ref({
+          variantListingConfig: {
+            displayParent: true,
+          },
+          parentId: null,
+          calculatedCheapestPrice: {
+            hasRange: true,
+          },
+        } as unknown as Schemas["Product"]),
+      ),
+    );
+    expect(vm.displayFrom).toBe(false);
+    expect(vm.isListPrice).toBe(false);
+  });
 });
diff --git a/packages/composables/src/useProductWishlist.test.ts b/packages/composables/src/useProductWishlist.test.ts
index b431e60b..91b2b1a3 100644
--- a/packages/composables/src/useProductWishlist.test.ts
+++ b/packages/composables/src/useProductWishlist.test.ts
@@ -32,6 +32,7 @@ describe("useProductWishlist", () => {
       const { vm } = useSetup(() => useProductWishlist("test3"));
 
       expect(vm.removeFromWishlist()).resolves.toEqual(undefined);
+      expect(vm.isInWishlist).toEqual(false);
     });
   });
 
@@ -50,9 +51,14 @@ describe("useProductWishlist", () => {
     });
 
     it("removeFromWishlist", () => {
+      vi.mocked(useUser).mockReturnValue({
+        isLoggedIn: ref(true),
+        isGuestSession: ref(false),
+      } as ReturnType<typeof useUser>);
       const { vm } = useSetup(() => useProductWishlist("test3"));
 
       expect(vm.removeFromWishlist()).resolves.toEqual(undefined);
+      expect(vm.isInWishlist).toEqual(false);
     });
   });
 });
diff --git a/packages/composables/src/useSyncWishlist.test.ts b/packages/composables/src/useSyncWishlist.test.ts
index fa261ccc..7c3b95f7 100644
--- a/packages/composables/src/useSyncWishlist.test.ts
+++ b/packages/composables/src/useSyncWishlist.test.ts
@@ -1,15 +1,19 @@
-import { describe, expect, it, vi } from "vitest";
+import { describe, expect, it, vi, beforeEach } from "vitest";
 import { useSyncWishlist } from "./useSyncWishlist";
 import { useSetup } from "./_test";
 import { ApiClientError } from "@shopware/api-client";
 import type { FetchResponse } from "ofetch";
 
 describe("useSyncWishlist", () => {
+  beforeEach(() => {
+    vi.clearAllMocks();
+  });
+
   const consoleErrorSpy = vi.spyOn(console, "error");
   consoleErrorSpy.mockImplementation(() => {});
   describe("methods", () => {
     describe("addToWishlist", () => {
-      it("wishlist add product", () => {
+      it("should add product to the wishlist", () => {
         const { vm } = useSetup(() => useSyncWishlist());
 
         expect(vm.addToWishlistSync("some-id")).resolves.toBe(undefined);
@@ -17,7 +21,7 @@ describe("useSyncWishlist", () => {
     });
 
     describe("removeFromWishlist", () => {
-      it("wishlist remove", () => {
+      it("should remove product from the wishlist", () => {
         const { vm } = useSetup(() => useSyncWishlist());
 
         vm.getWishlistProducts();
@@ -28,7 +32,7 @@ describe("useSyncWishlist", () => {
     });
 
     describe("mergeWishlistProducts", () => {
-      it("wishlist remove", () => {
+      it("should sync wishlist", () => {
         const { vm } = useSetup(() => useSyncWishlist());
 
         expect(vm.mergeWishlistProducts(["test1", "test2"])).resolves.toEqual(
@@ -38,17 +42,53 @@ describe("useSyncWishlist", () => {
     });
 
     describe("getWishlistProducts", () => {
-      it("getWishlistProducts", () => {
-        const { vm } = useSetup(() => useSyncWishlist());
+      it("should get wishlist products", async () => {
+        const { vm, injections } = useSetup(() => useSyncWishlist());
+        injections.apiClient.invoke.mockResolvedValue({
+          data: {
+            products: {
+              elements: [],
+            },
+          },
+        });
+        await vm.getWishlistProducts();
 
-        vm.getWishlistProducts();
         expect(vm.count).toBe(0);
         expect(vm.items.length).toBe(0);
       });
+
+      it("getWishlistProducts", async () => {
+        const { vm, injections } = useSetup(() => useSyncWishlist());
+        injections.apiClient.invoke.mockResolvedValue({
+          data: {
+            products: {
+              total: 9,
+              page: 1,
+              elements: [
+                {
+                  id: "test-id",
+                },
+              ],
+            },
+          },
+        });
+        await vm.getWishlistProducts();
+
+        expect(injections.apiClient.invoke).toHaveBeenCalledWith(
+          expect.stringContaining("readCustomerWishlist"),
+          expect.objectContaining({
+            body: {
+              "total-count-mode": "exact",
+            },
+          }),
+        );
+        expect(vm.count).toBe(9);
+        expect(vm.items.length).toBe(1);
+      });
     });
 
     describe("getWishlistProducts - error", () => {
-      it("getWishlistProducts", async () => {
+      it("should handle error after fetching product wishlist", async () => {
         const { vm } = useSetup(() => useSyncWishlist(), {
           apiClient: {
             invoke: vi.fn().mockImplementation(() => {
@@ -69,7 +109,7 @@ describe("useSyncWishlist", () => {
         });
         vm.getWishlistProducts();
         expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
-        expect(vm.count).toBe(0);
+        expect(vm.count).toBe(9);
         expect(vm.items.length).toBe(0);
       });
     });
diff --git a/packages/composables/src/useWishlist.test.ts b/packages/composables/src/useWishlist.test.ts
index a5c393d1..7ae3d24f 100644
--- a/packages/composables/src/useWishlist.test.ts
+++ b/packages/composables/src/useWishlist.test.ts
@@ -47,13 +47,11 @@ vi.mock("./useSyncWishlist.ts");
 vi.mock("./useUser");
 
 describe("useWishlist - not logged in user", () => {
-  it("mergeWishlistProducts", () => {
+  it("should merge wishlist products", () => {
     vi.mocked(useUser).mockReturnValue(getMockedUser(false, true));
     vi.mocked(useSyncWishlist).mockReturnValue({
       getWishlistProducts: () => new Promise<void>((resolve) => resolve()),
-      items: computed((): string[] => {
-        return ["test1"];
-      }),
+      items: computed((): string[] => []),
       addToWishlistSync: () => undefined,
       mergeWishlistProducts: () => undefined,
       removeFromWishlistSync: () => undefined,
@@ -71,13 +69,11 @@ describe("useWishlist - not logged in user", () => {
     expect(Number(vm.totalPagesCount)).toBe(2);
   });
 
-  it("getWishlistProducts", async () => {
+  it("should get wishlist products", async () => {
     vi.mocked(useUser).mockReturnValue(getMockedUser(false, true));
     vi.mocked(useSyncWishlist).mockReturnValue({
       getWishlistProducts: () => new Promise<void>((resolve) => resolve()),
-      items: computed((): string[] => {
-        return ["test1"];
-      }),
+      items: computed((): string[] => ["test1"]),
       addToWishlistSync: () => undefined,
       mergeWishlistProducts: () => undefined,
       removeFromWishlistSync: () => undefined,
@@ -119,7 +115,7 @@ describe("useWishlist - not logged in user", () => {
 });
 
 describe("useWishlist - logged in user", () => {
-  it("mergeWishlistProducts", () => {
+  it("should merge wishlist products", () => {
     vi.mocked(useUser).mockReturnValue(getMockedUser(true, false));
     vi.mocked(useSyncWishlist).mockReturnValue({
       getWishlistProducts: () => new Promise<void>((resolve) => resolve()),
@@ -158,7 +154,7 @@ describe("useWishlist - logged in user", () => {
     expect(vm.count).toBe(15);
   });
 
-  it("getWishlistProducts", () => {
+  it("should get wishlist products", () => {
     vi.mocked(useUser).mockReturnValue(getMockedUser(true, false));
     const { vm } = useSetup(() => useWishlist());
     vm.getWishlistProducts();
diff --git a/packages/composables/vitest.config.ts b/packages/composables/vitest.config.ts
index 72ac8f88..6c34acd7 100644
--- a/packages/composables/vitest.config.ts
+++ b/packages/composables/vitest.config.ts
@@ -7,14 +7,17 @@ export default defineConfig({
     coverage: {
       enabled: true,
       include: ["src"],
-      // "100": true, // TODO: our goal ;)
       thresholds: {
-        statements: 97,
-        branches: 90,
-        functions: 88.4,
-        lines: 97,
+        100: true,
       },
-      exclude: ["**/devtools/**"],
+      exclude: [
+        "**/devtools/**",
+        "**/types/**",
+        "**/src/*.test.ts",
+        "**/src/*.spec.ts",
+        // After refactoring should be removed - https://github.com/shopware/frontends/issues/1057
+        "**/src/useListing.ts",
+      ],
     },
     alias: {
       "#imports": resolve(__dirname, "./src/index.ts"),