Repository: shopware/frontends
Pull request #307 (2023-07-10 07:29:14)

fix(vue-demo-store): extract lang code from accept-language header
### Description

Browser can have a setting with multilanguages allowed as an accept-language header `"de,en-US;q=0.7,en;q=0.3";`
the order sticks to the priority. 
thanks to this we can find the first matching language code from the string, and then, the language code can be used by 
```
new Intl.NumberFormat('de', {
      style: "currency",
      currency: 'EUR',
    }).format(123)
'123,00 €'
```

### Type of change

<!--
  Please select the relevant option.
  Remember to run `pnpm run changeset` and describe your change (plus potential migration guide/important notes) to your pull request. Read more: https://github.com/shopware/frontends/blob/main/CONTRIBUTION.md#changelog-preparation
-->

- [x] Bug fix (non-breaking change which 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)

### 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]: #RnyAvOsTdadUMFX27oCwP0vIyylkztNsBSg30jhfZjM=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJzaG9wd2FyZS1mcm9udGVuZHMtZG9jcyIsInJvb3REaXJlY3RvcnkiOm51bGwsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zaG9wd2FyZS1mcm9udGVuZHMvc2hvcHdhcmUtZnJvbnRlbmRzLWRvY3MvNVNzOTlNYnp1elR5SGZxd2h2b1VodDR0akExeSIsInByZXZpZXdVcmwiOiJzaG9wd2FyZS1mcm9udGVuZHMtZG9jcy1naXQtZml4LXVzZS1wLTVkZTk3Zi1zaG9wd2FyZS1mcm9udGVuZHMudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCJ9LHsibmFtZSI6ImZyb250ZW5kcy1kZW1vIiwicm9vdERpcmVjdG9yeSI6InRlbXBsYXRlcy92dWUtZGVtby1zdG9yZSIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zaG9wd2FyZS1mcm9udGVuZHMvZnJvbnRlbmRzLWRlbW8vN2Z5OTI3d0dQdHc5M3MxQ0x6c1lkTU1kbnZUTCIsInByZXZpZXdVcmwiOiJmcm9udGVuZHMtZGVtby1naXQtZml4LXVzZS1wcmljZS1sb2NhLTI5ZWRmNi1zaG9wd2FyZS1mcm9udGVuZHMudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCJ9XX0=
**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** | ✅ Ready ([Inspect](https://vercel.com/shopware-frontends/frontends-demo/7fy927wGPtw93s1CLzsYdMMdnvTL)) | [Visit Preview](https://frontends-demo-git-fix-use-price-loca-29edf6-shopware-frontends.vercel.app) | Jul 10, 2023 7:29am |
| **shopware-frontends-docs** | ✅ Ready ([Inspect](https://vercel.com/shopware-frontends/shopware-frontends-docs/5Ss99MbzuzTyHfqwhvoUht4tjA1y)) | [Visit Preview](https://shopware-frontends-docs-git-fix-use-p-5de97f-shopware-frontends.vercel.app) | Jul 10, 2023 7:29am |




Patch:
diff --git a/.changeset/itchy-kiwis-sin.md b/.changeset/itchy-kiwis-sin.md
new file mode 100644
index 00000000..97d9456f
--- /dev/null
+++ b/.changeset/itchy-kiwis-sin.md
@@ -0,0 +1,5 @@
+---
+"vue-demo-store": patch
+---
+
+Extract lang code from accept-language header in SSR for Intl.NumberFormat purposes
diff --git a/.changeset/sixty-hornets-turn.md b/.changeset/sixty-hornets-turn.md
new file mode 100644
index 00000000..0169d1c5
--- /dev/null
+++ b/.changeset/sixty-hornets-turn.md
@@ -0,0 +1,22 @@
+---
+"@shopware/api-client": minor
+---
+
+Improved error handling. Api client now throws `ApiClientError` with detailed information about what went wrong with request.
+
+example:
+
+```typescript
+import { ApiClientError } from "@shopware/api-client";
+
+try {
+  // ... your request
+} catch (error) {
+  if (error instanceof ApiClientError) {
+    console.error(error); // This prints message summary
+    console.error("Details:", error.details); // Raw response from API
+  } else {
+    console.error("==>", error); // Another type of error, not recognized by API client
+  }
+}
+```
diff --git a/packages/api-client-next/README.md b/packages/api-client-next/README.md
index fbd95b21..e4f4f6cd 100644
--- a/packages/api-client-next/README.md
+++ b/packages/api-client-next/README.md
@@ -115,6 +115,25 @@ async function loadMainNavigation() {
 }
 ```
 
+### Error handling
+
+Client is throwing `ApiClientError` with detailed information returned from the API. It will display clear message in the console or you can access `details` property to get raw information from the response.
+
+```typescript
+import { ApiClientError } from "@shopware/api-client";
+
+try {
+  // ... your request
+} catch (error) {
+  if (error instanceof ApiClientError) {
+    console.error(error); // This prints message summary
+    console.error("Details:", error.details); // Raw response from API
+  } else {
+    console.error("==>", error); // Another type of error, not recognized by API client
+  }
+}
+```
+
 ## Links
 
 - [📘 Documentation](https://frontends.shopware.com)
diff --git a/packages/api-client-next/src/errorInterceptor.ts b/packages/api-client-next/src/errorInterceptor.ts
new file mode 100644
index 00000000..6382c15e
--- /dev/null
+++ b/packages/api-client-next/src/errorInterceptor.ts
@@ -0,0 +1,73 @@
+import { FetchResponse } from "ofetch";
+
+export type ApiError = {
+  title?: string;
+  detail?: string;
+  code?: string;
+  status?: string;
+  source?: {
+    pointer?: string;
+  };
+  meta?: {
+    parameters?: Record<string, string>;
+  };
+};
+
+export class ApiClientError<
+  T extends { errors: Array<ApiError> },
+> extends Error {
+  /**
+   * Flag to indicate if the request was successful.
+   */
+  public ok: boolean;
+  /**
+   * HTTP status code of the response.
+   */
+  public status: number;
+  /**
+   * HTTP status text of the response.
+   */
+  public statusText?: string;
+  /**
+   * URL of the request.
+   */
+  public url: string;
+  /**
+   * Details of the error.
+   */
+  public details: T;
+  /**
+   * Headers of the response.
+   */
+  public headers: Headers;
+
+  constructor(response: FetchResponse<T>) {
+    let message = "Failed request";
+    message += response._data?.errors.reduce((message, error) => {
+      let pointer = "";
+      if (error.source?.pointer) {
+        pointer = `[${error.source.pointer}]`;
+      }
+      return `${message}\n - [${error.title}]${pointer} ${error.detail}`;
+    }, "");
+    super(message);
+
+    this.name = "ApiClientError";
+    this.details =
+      response._data ||
+      ({
+        errors: [{ title: "Unknown error", detail: "" }],
+      } as T);
+    this.ok = response.ok;
+    this.status = response.status;
+    this.statusText = response.statusText;
+    this.url = response.url;
+    this.headers = response.headers;
+  }
+}
+
+export function errorInterceptor<T extends { errors: Array<ApiError> }>(
+  response: FetchResponse<T>,
+) {
+  throw new ApiClientError(response);
+}
diff --git a/packages/api-client-next/src/index.ts b/packages/api-client-next/src/index.ts
index 81c1fe09..2991ddde 100644
--- a/packages/api-client-next/src/index.ts
+++ b/packages/api-client-next/src/index.ts
@@ -3,6 +3,7 @@ import {
   operations as defaultOperations,
   paths as defaultPaths,
 } from "../api-types";
+import { errorInterceptor } from "./errorInterceptor";
 
 type Operations = Record<string, unknown>;
 
@@ -89,7 +90,9 @@ export function createAPIClient<
         params.onContextChanged?.(newContextToken);
       }
     },
-    // async onResponseError({ request, response, options }) {},
+    async onResponseError({ request, response, options }) {
+      errorInterceptor(response);
+    },
   });
 
   /**
@@ -188,3 +191,5 @@ export function transformPathToQuery<T extends Record<string, unknown>>(
 
   return [requestPathWithParams, returnOptions];
 }
+
+export { ApiClientError } from "./errorInterceptor";
diff --git a/packages/composables/src/mocks/LoginResponse.ts b/packages/composables/src/mocks/LoginResponse.ts
new file mode 100644
index 00000000..6665b294
--- /dev/null
+++ b/packages/composables/src/mocks/LoginResponse.ts
@@ -0,0 +1,9 @@
+import { ContextUpdateResult } from "@shopware-pwa/types";
+
+const LoginResponse: ContextUpdateResult = {
+  apiAlias: "array_struct",
+  contextToken: "CK273DThPuUSoG6g76qBAncveqQ9h5I3",
+  redirectUrl: null,
+};
+
+export default LoginResponse;
diff --git a/packages/composables/src/mocks/Menu.ts b/packages/composables/src/mocks/Menu.ts
new file mode 100644
index 00000000..2516a115
--- /dev/null
+++ b/packages/composables/src/mocks/Menu.ts
@@ -0,0 +1,598 @@
+import { StoreNavigationElement } from "@shopware-pwa/types";
+
+const Menu: StoreNavigationElement[] = [
+  {
+    extensions: {
+      search: {
+        extensions: [],
+        _uniqueIdentifier: null,
+        translated: [],
+        autoIncrement: 26,
+      },
+    },
+    versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",
+    translated: {
+      breadcrumb: ["Home", "Footer Navigation", "Shop service "],
+      name: "Shop service ",
+      customFields: {},
+      linkType: null,
+      internalLink: null,
+      externalLink: null,
+      linkNewTab: null,
+      description: null,
+      metaTitle: null,
+      metaDescription: null,
+      keywords: null,
+    },
+    createdAt: "2020-08-06T06:26:25.159+00:00",
+    updatedAt: null,
+    afterCategoryId: null,
+    parentId: "0208fc353bdb492aba4bbd910ea5cc5a",
+    mediaId: null,
+    name: "Shop service ",
+    breadcrumb: ["Home", "Footer Navigation", "Shop service "],
+    path: "|525abe8981214bd2ba94fd33942333ec|0208fc353bdb492aba4bbd910ea5cc5a|",
+    level: 3,
+    active: true,
+    childCount: 3,
+    visibleChildCount: 2,
+    displayNestedProducts: true,
+    parent: null,
+    children: [
+      {
+        extensions: {
+          search: {
+            extensions: [],
+            _uniqueIdentifier: null,
+            translated: [],
+            autoIncrement: 31,
+          },
+        },
+        versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",
+        translated: {
+          breadcrumb: [
+            "Home",
+            "Footer Navigation",
+            "Shop service ",
+            "Defective Product",
+          ],
+          name: "Defective Product",
+          customFields: {},
+          linkType: null,
+          internalLink: null,
+          externalLink: null,
+          linkNewTab: null,
+          description: null,
+          metaTitle: null,
+          metaDescription: null,
+          keywords: null,
+        },
+        createdAt: "2020-08-06T06:26:25.218+00:00",
+        updatedAt: null,
+        afterCategoryId: null,
+        parentId: "c568b6d678014d9bbc5e1e4b684b79c7",
+        mediaId: null,
+        name: "Defective Product",
+        breadcrumb: [
+          "Home",
+          "Footer Navigation",
+          "Shop service ",
+          "Defective Product",
+        ],
+        path: "|525abe8981214bd2ba94fd33942333ec|0208fc353bdb492aba4bbd910ea5cc5a|c568b6d678014d9bbc5e1e4b684b79c7|",
+        level: 4,
+        active: true,
+        childCount: 0,
+        visibleChildCount: 0,
+        displayNestedProducts: true,
+        parent: null,
+        children: [],
+        translations: null,
+        media: null,
+        cmsPageId: "cafbc9662dcd462a941c6fd8b284c462",
+        cmsPageIdSwitched: false,
+        cmsPage: null,
+        linkType: null,
+        linkNewTab: null,
+        internalLink: null,
+        externalLink: null,
+        visible: true,
+        type: "page",
+        productAssignmentType: "product",
+        description: null,
+        metaTitle: null,
+        metaDescription: null,
+        keywords: null,
+        seoUrls: [
+          {
+            translated: [],
+            createdAt: "2022-08-01T14:02:03.482+00:00",
+            updatedAt: "2022-08-03T08:06:59.193+00:00",
+            salesChannelId: "98432def39fc4624b33213a56b8c944d",
+            languageId: "2fbb5fe2e29a4d70aa5854ce7ce3e20b",
+            routeName: "frontend.navigation.page",
+            foreignKey: "a8ac011151204f10bc3a3bf32ef3f29d",
+            pathInfo: "/navigation/a8ac011151204f10bc3a3bf32ef3f29d",
+            seoPathInfo: "Shop-service/Defective-Product/",
+            isCanonical: true,
+            isModified: false,
+            isDeleted: false,
+            isValid: null,
+            url: null,
+            customFields: null,
+            id: "5bc3ec6562b24fbdbf520af312d39889",
+            apiAlias: "seo_url",
+          },
+        ],
+        customEntityTypeId: null,
+        customFields: null,
+        id: "a8ac011151204f10bc3a3bf32ef3f29d",
+        apiAlias: "category",
+      },
+      {
+        extensions: {
+          search: {
+            extensions: [],
+            _uniqueIdentifier: null,
+            translated: [],
+            autoIncrement: 44,
+          },
+        },
+        versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",
+        translated: {
+          breadcrumb: [
+            "Home",
+            "Footer Navigation",
+            "Shop service ",
+            "Payment / Dispatch",
+          ],
+          name: "Payment / Dispatch",
+          customFields: {},
+          linkType: null,
+          internalLink: null,
+          externalLink: null,
+          linkNewTab: null,
+          description: null,
+          metaTitle: null,
+          metaDescription: null,
+          keywords: null,
+        },
+        createdAt: "2020-08-06T06:26:25.371+00:00",
+        updatedAt: null,
+        afterCategoryId: "a8ac011151204f10bc3a3bf32ef3f29d",
+        parentId: "c568b6d678014d9bbc5e1e4b684b79c7",
+        mediaId: null,
+        name: "Payment / Dispatch",
+        breadcrumb: [
+          "Home",
+          "Footer Navigation",
+          "Shop service ",
+          "Payment / Dispatch",
+        ],
+        path: "|525abe8981214bd2ba94fd33942333ec|0208fc353bdb492aba4bbd910ea5cc5a|c568b6d678014d9bbc5e1e4b684b79c7|",
+        level: 4,
+        active: true,
+        childCount: 0,
+        visibleChildCount: 0,
+        displayNestedProducts: true,
+        parent: null,
+        children: [],
+        translations: null,
+        media: null,
+        cmsPageId: "cafbc9662dcd462a941c6fd8b284c462",
+        cmsPageIdSwitched: false,
+        cmsPage: null,
+        linkType: null,
+        linkNewTab: null,
+        internalLink: null,
+        externalLink: null,
+        visible: true,
+        type: "page",
+        productAssignmentType: "product",
+        description: null,
+        metaTitle: null,
+        metaDescription: null,
+        keywords: null,
+        seoUrls: [
+          {
+            translated: [],
+            createdAt: "2022-08-01T14:02:03.482+00:00",
+            updatedAt: "2022-08-03T08:06:59.193+00:00",
+            salesChannelId: "98432def39fc4624b33213a56b8c944d",
+            languageId: "2fbb5fe2e29a4d70aa5854ce7ce3e20b",
+            routeName: "frontend.navigation.page",
+            foreignKey: "a19b6eddb87a4a8d80828997ad3f7fd4",
+            pathInfo: "/navigation/a19b6eddb87a4a8d80828997ad3f7fd4",
+            seoPathInfo: "Shop-service/Payment-Dispatch/",
+            isCanonical: true,
+            isModified: false,
+            isDeleted: false,
+            isValid: null,
+            url: null,
+            customFields: null,
+            id: "19f5bda939cb4147bc71ac0efa1573a4",
+            apiAlias: "seo_url",
+          },
+        ],
+        customEntityTypeId: null,
+        customFields: null,
+        id: "a19b6eddb87a4a8d80828997ad3f7fd4",
+        apiAlias: "category",
+      },
+    ],
+    translations: null,
+    media: null,
+    cmsPageId: "c7feafffb4404ebfb4090ffa328d2c7c",
+    cmsPageIdSwitched: true,
+    cmsPage: null,
+    linkType: null,
+    linkNewTab: null,
+    internalLink: null,
+    externalLink: null,
+    visible: true,
+    type: "page",
+    productAssignmentType: "product",
+    description: null,
+    metaTitle: null,
+    metaDescription: null,
+    keywords: null,
+    seoUrls: [
+      {
+        translated: [],
+        createdAt: "2022-08-01T14:02:03.482+00:00",
+        updatedAt: "2022-08-03T08:06:59.193+00:00",
+        salesChannelId: "98432def39fc4624b33213a56b8c944d",
+        languageId: "2fbb5fe2e29a4d70aa5854ce7ce3e20b",
+        routeName: "frontend.navigation.page",
+        foreignKey: "c568b6d678014d9bbc5e1e4b684b79c7",
+        pathInfo: "/navigation/c568b6d678014d9bbc5e1e4b684b79c7",
+        seoPathInfo: "Shop-service/",
+        isCanonical: true,
+        isModified: false,
+        isDeleted: false,
+        isValid: null,
+        url: null,
+        customFields: null,
+        id: "3b2716a1648649bd8a8d93eb326bea3d",
+        apiAlias: "seo_url",
+      },
+    ],
+    customEntityTypeId: null,
+    customFields: null,
+    id: "c568b6d678014d9bbc5e1e4b684b79c7",
+    apiAlias: "category",
+  },
+  {
+    extensions: {
+      search: {
+        extensions: [],
+        _uniqueIdentifier: null,
+        translated: [],
+        autoIncrement: 38,
+      },
+    },
+    versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",
+    translated: {
+      breadcrumb: ["Home", "Footer Navigation", "Information"],
+      name: "Information",
+      customFields: {},
+      linkType: null,
+      internalLink: null,
+      externalLink: null,
+      linkNewTab: null,
+      description: null,
+      metaTitle: null,
+      metaDescription: null,
+      keywords: null,
+    },
+    createdAt: "2020-08-06T06:26:25.300+00:00",
+    updatedAt: null,
+    afterCategoryId: "c568b6d678014d9bbc5e1e4b684b79c7",
+    parentId: "0208fc353bdb492aba4bbd910ea5cc5a",
+    mediaId: null,
+    name: "Information",
+    breadcrumb: ["Home", "Footer Navigation", "Information"],
+    path: "|525abe8981214bd2ba94fd33942333ec|0208fc353bdb492aba4bbd910ea5cc5a|",
+    level: 3,
+    active: true,
+    childCount: 3,
+    visibleChildCount: 3,
+    displayNestedProducts: true,
+    parent: null,
+    children: [
+      {
+        extensions: {
+          search: {
+            extensions: [],
+            _uniqueIdentifier: null,
+            translated: [],
+            autoIncrement: 43,
+          },
+        },
+        versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",
+        translated: {
+          breadcrumb: [
+            "Home",
+            "Footer Navigation",
+            "Information",
+            "General terms \u0026 conditions",
+          ],
+          name: "General terms \u0026 conditions",
+          customFields: {},
+          linkType: null,
+          internalLink: null,
+          externalLink: null,
+          linkNewTab: null,
+          description: null,
+          metaTitle: null,
+          metaDescription: null,
+          keywords: null,
+        },
+        createdAt: "2020-08-06T06:26:25.359+00:00",
+        updatedAt: "2022-08-01T03:47:22.714+00:00",
+        afterCategoryId: null,
+        parentId: "162cd978ddaa498790e70be6dc75d089",
+        mediaId: null,
+        name: "General terms \u0026 conditions",
+        breadcrumb: [
+          "Home",
+          "Footer Navigation",
+          "Information",
+          "General terms \u0026 conditions",
+        ],
+        path: "|525abe8981214bd2ba94fd33942333ec|0208fc353bdb492aba4bbd910ea5cc5a|162cd978ddaa498790e70be6dc75d089|",
+        level: 4,
+        active: true,
+        childCount: 0,
+        visibleChildCount: 0,
+        displayNestedProducts: true,
+        parent: null,
+        children: [],
+        translations: null,
+        media: null,
+        cmsPageId: "cafbc9662dcd462a941c6fd8b284c462",
+        cmsPageIdSwitched: false,
+        cmsPage: null,
+        linkType: null,
+        linkNewTab: null,
+        internalLink: null,
+        externalLink: null,
+        visible: true,
+        type: "page",
+        productAssignmentType: "product",
+        description: null,
+        metaTitle: null,
+        metaDescription: null,
+        keywords: null,
+        seoUrls: [
+          {
+            translated: [],
+            createdAt: "2023-05-22T12:15:30.642+00:00",
+            updatedAt: null,
+            salesChannelId: "98432def39fc4624b33213a56b8c944d",
+            languageId: "2fbb5fe2e29a4d70aa5854ce7ce3e20b",
+            routeName: "frontend.navigation.page",
+            foreignKey: "7318eb39105f4c92b15ac2f84a252169",
+            pathInfo: "/navigation/7318eb39105f4c92b15ac2f84a252169",
+            seoPathInfo: "Information/General-terms-conditions/",
+            isCanonical: true,
+            isModified: false,
+            isDeleted: false,
+            isValid: null,
+            url: null,
+            customFields: null,
+            id: "ec1162dfa21744e18f12aef1c334a3d1",
+            apiAlias: "seo_url",
+          },
+        ],
+        customEntityTypeId: null,
+        customFields: {},
+        id: "7318eb39105f4c92b15ac2f84a252169",
+        apiAlias: "category",
+      },
+      {
+        extensions: {
+          search: {
+            extensions: [],
+            _uniqueIdentifier: null,
+            translated: [],
+            autoIncrement: 96,
+          },
+        },
+        versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",
+        translated: {
+          breadcrumb: ["Home", "Footer Navigation", "Information", "Contact"],
+          name: "Contact",
+          customFields: {},
+          linkType: null,
+          internalLink: null,
+          externalLink: null,
+          linkNewTab: null,
+          description: null,
+          metaTitle: null,
+          metaDescription: null,
+          keywords: null,
+        },
+        createdAt: "2023-06-06T08:24:43.018+00:00",
+        updatedAt: "2023-06-06T08:25:43.398+00:00",
+        afterCategoryId: "7318eb39105f4c92b15ac2f84a252169",
+        parentId: "162cd978ddaa498790e70be6dc75d089",
+        mediaId: null,
+        name: "Contact",
+        breadcrumb: ["Home", "Footer Navigation", "Information", "Contact"],
+        path: "|525abe8981214bd2ba94fd33942333ec|0208fc353bdb492aba4bbd910ea5cc5a|162cd978ddaa498790e70be6dc75d089|",
+        level: 4,
+        active: true,
+        childCount: 0,
+        visibleChildCount: 0,
+        displayNestedProducts: true,
+        parent: null,
+        children: [],
+        translations: null,
+        media: null,
+        cmsPageId: "76ed25ab9c694873948f9183a8979a71",
+        cmsPageIdSwitched: false,
+        cmsPage: null,
+        linkType: null,
+        linkNewTab: null,
+        internalLink: null,
+        externalLink: null,
+        visible: true,
+        type: "page",
+        productAssignmentType: "product",
+        description: null,
+        metaTitle: null,
+        metaDescription: null,
+        keywords: null,
+        seoUrls: [
+          {
+            translated: [],
+            createdAt: "2023-06-06T08:25:43.629+00:00",
+            updatedAt: null,
+            salesChannelId: "98432def39fc4624b33213a56b8c944d",
+            languageId: "2fbb5fe2e29a4d70aa5854ce7ce3e20b",
+            routeName: "frontend.navigation.page",
+            foreignKey: "829d37718fe84e9d89bf377c197a029e",
+            pathInfo: "/navigation/829d37718fe84e9d89bf377c197a029e",
+            seoPathInfo: "Information/Contact/",
+            isCanonical: true,
+            isModified: false,
+            isDeleted: false,
+            isValid: null,
+            url: null,
+            customFields: null,
+            id: "9840d61b7e304f3cbf997c1ecdd3339e",
+            apiAlias: "seo_url",
+          },
+        ],
+        customEntityTypeId: null,
+        customFields: null,
+        id: "829d37718fe84e9d89bf377c197a029e",
+        apiAlias: "category",
+      },
+      {
+        extensions: {
+          search: {
+            extensions: [],
+            _uniqueIdentifier: null,
+            translated: [],
+            autoIncrement: 52,
+          },
+        },
+        versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",
+        translated: {
+          breadcrumb: ["Home", "Footer Navigation", "Information", "Imprint"],
+          name: "Imprint",
+          customFields: {},
+          linkType: null,
+          internalLink: null,
+          externalLink: null,
+          linkNewTab: null,
+          description: null,
+          metaTitle: null,
+          metaDescription: null,
+          keywords: null,
+        },
+        createdAt: "2020-08-06T06:26:25.465+00:00",
+        updatedAt: "2023-06-06T08:24:47.486+00:00",
+        afterCategoryId: "829d37718fe84e9d89bf377c197a029e",
+        parentId: "162cd978ddaa498790e70be6dc75d089",
+        mediaId: null,
+        name: "Imprint",
+        breadcrumb: ["Home", "Footer Navigation", "Information", "Imprint"],
+        path: "|525abe8981214bd2ba94fd33942333ec|0208fc353bdb492aba4bbd910ea5cc5a|162cd978ddaa498790e70be6dc75d089|",
+        level: 4,
+        active: true,
+        childCount: 0,
+        visibleChildCount: 0,
+        displayNestedProducts: true,
+        parent: null,
+        children: [],
+        translations: null,
+        media: null,
+        cmsPageId: "cafbc9662dcd462a941c6fd8b284c462",
+        cmsPageIdSwitched: false,
+        cmsPage: null,
+        linkType: null,
+        linkNewTab: null,
+        internalLink: null,
+        externalLink: null,
+        visible: true,
+        type: "page",
+        productAssignmentType: "product",
+        description: null,
+        metaTitle: null,
+        metaDescription: null,
+        keywords: null,
+        seoUrls: [
+          {
+            translated: [],
+            createdAt: "2023-05-22T12:15:30.642+00:00",
+            updatedAt: null,
+            salesChannelId: "98432def39fc4624b33213a56b8c944d",
+            languageId: "2fbb5fe2e29a4d70aa5854ce7ce3e20b",
+            routeName: "frontend.navigation.page",
+            foreignKey: "0c1493c7e7ec4961a393f18cf0310522",
+            pathInfo: "/navigation/0c1493c7e7ec4961a393f18cf0310522",
+            seoPathInfo: "Information/Imprint/",
+            isCanonical: true,
+            isModified: false,
+            isDeleted: false,
+            isValid: null,
+            url: null,
+            customFields: null,
+            id: "a9d9439488e045f1b496c6a09809189a",
+            apiAlias: "seo_url",
+          },
+        ],
+        customEntityTypeId: null,
+        customFields: null,
+        id: "0c1493c7e7ec4961a393f18cf0310522",
+        apiAlias: "category",
+      },
+    ],
+    translations: null,
+    media: null,
+    cmsPageId: "cafbc9662dcd462a941c6fd8b284c462",
+    cmsPageIdSwitched: false,
+    cmsPage: null,
+    linkType: null,
+    linkNewTab: null,
+    internalLink: null,
+    externalLink: null,
+    visible: true,
+    type: "page",
+    productAssignmentType: "product",
+    description: null,
+    metaTitle: null,
+    metaDescription: null,
+    keywords: null,
+    seoUrls: [
+      {
+        translated: [],
+        createdAt: "2022-08-01T14:02:03.482+00:00",
+        updatedAt: "2022-08-03T08:06:59.193+00:00",
+        salesChannelId: "98432def39fc4624b33213a56b8c944d",
+        languageId: "2fbb5fe2e29a4d70aa5854ce7ce3e20b",
+        routeName: "frontend.navigation.page",
+        foreignKey: "162cd978ddaa498790e70be6dc75d089",
+        pathInfo: "/navigation/162cd978ddaa498790e70be6dc75d089",
+        seoPathInfo: "Information/",
+        isCanonical: true,
+        isModified: false,
+        isDeleted: false,
+        isValid: null,
+        url: null,
+        customFields: null,
+        id: "29b38ac4ebbc413ca1af745503cdc890",
+        apiAlias: "seo_url",
+      },
+    ],
+    customEntityTypeId: null,
+    customFields: null,
+    id: "162cd978ddaa498790e70be6dc75d089",
+    apiAlias: "category",
+  },
+];
+
+export default Menu;
diff --git a/packages/composables/src/mocks/Product.ts b/packages/composables/src/mocks/Product.ts
new file mode 100644
index 00000000..115b5908
--- /dev/null
+++ b/packages/composables/src/mocks/Product.ts
@@ -0,0 +1,563 @@
+import { Product } from "@shopware-pwa/types";
+
+const product: Product = {
+  versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",
+  apiAlias: "product",
+  autoIncrement: 1,
+  calculatedListingPrice: null,
+  cheapestPrice: null,
+  listingPrices: null,
+  extensions: [],
+  productManufacturerVersionId: null,
+  purchasePrice: null,
+  seoCategory: null,
+  parentVersionId: null,
+  price: null,
+  prices: null,
+  tagIds: null,
+  tags: null,
+  translated: {
+    metaDescription: null,
+    name: "Cigar Special 40%",
+    keywords: null,
+    description:
+      "\u003Cp\u003ELorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.\u003C/p\u003E\n\u003Cp\u003EIn enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar.\u003C/p\u003E",
+    metaTitle: null,
+    packUnit: "Flasche(n)",
+    packUnitPlural: null,
+    customFields: {},
+  },
+  createdAt: "2020-08-06T06:26:45.773+00:00",
+  updatedAt: "2023-06-26T20:20:14.785+00:00",
+  parentId: "c349b26a505c46ca980efbe2f3b71607",
+  childCount: null,
+  taxId: "d55571c0b59a4a538fe44f16a3a206c3",
+  manufacturerId: "ed811aac3d264b35949e6808670e1c8c",
+  unitId: "0e0be7e6c06a4ebcba222c306986065a",
+  active: true,
+  displayGroup: "dadaf7a913c869100c0de0c074876353",
+  manufacturerNumber: null,
+  ean: null,
+  sales: 0,
+  productNumber: "SW10044",
+  stock: 24,
+  availableStock: 19,
+  available: true,
+  deliveryTimeId: "0caaf7f6131142f6b1fa81f3e3f6c0fb",
+  deliveryTime: {
+    translated: {
+      name: "1-3 days",
+      customFields: {},
+    },
+    createdAt: "2020-08-06T06:26:54.178+00:00",
+    updatedAt: "2020-08-06T06:26:54.182+00:00",
+    name: "1-3 days",
+    min: 1,
+    max: 3,
+    unit: "day",
+    customFields: null,
+    id: "0caaf7f6131142f6b1fa81f3e3f6c0fb",
+    apiAlias: "delivery_time",
+  },
+  restockTime: 1,
+  isCloseout: false,
+  purchaseSteps: 1,
+  maxPurchase: null,
+  minPurchase: 1,
+  purchaseUnit: 0.2,
+  referenceUnit: 1,
+  shippingFree: false,
+  markAsTopseller: false,
+  weight: null,
+  width: null,
+  height: null,
+  length: null,
+  releaseDate: null,
+  categoryTree: [
+    "525abe8981214bd2ba94fd33942333ec",
+    "bda4b60e845240b2b9d6b60e71196e14",
+    "ca15f1e906204da49afe666bbcca7825",
+    "54a3e428361147c0a3372d44070fb84d",
+    "2b91ba3b1b8d40658eda14f62450152a",
+  ],
+  streamIds: [
+    "1318833f46df457981763b94179d9ef0",
+    "5675448d6b3a4dd2a659d31a3dde74da",
+  ],
+  optionIds: ["c3979c6ac5844104afa113c4fbde87f8"],
+  propertyIds: null,
+  name: "Cigar Special 40%",
+  keywords: null,
+  description:
+    "\u003Cp\u003ELorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.\u003C/p\u003E\n\u003Cp\u003EIn enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar.\u003C/p\u003E",
+  metaDescription: null,
+  metaTitle: null,
+  packUnit: "Flasche(n)",
+  packUnitPlural: null,
+  tax: {
+    translated: [],
+    createdAt: "2020-08-06T06:23:32.056+00:00",
+    updatedAt: "2020-08-06T06:26:46.462+00:00",
+    taxRate: 19,
+    name: "19%",
+    position: 0,
+    customFields: null,
+    id: "d55571c0b59a4a538fe44f16a3a206c3",
+    apiAlias: "tax",
+  },
+  manufacturer: null,
+  unit: {
+    translated: {
+      shortCode: "l",
+      name: "litre",
+      customFields: {},
+    },
+    createdAt: "2020-08-06T06:26:30.695+00:00",
+    updatedAt: null,
+    shortCode: "l",
+    name: "litre",
+    customFields: null,
+    id: "0e0be7e6c06a4ebcba222c306986065a",
+    apiAlias: "unit",
+  },
+  cover: {
+    versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",
+    translated: [],
+    createdAt: "2020-08-06T06:26:45.773+00:00",
+    updatedAt: "2023-06-26T20:20:14.427+00:00",
+    productId: "7e117859f69b4ee08947e1ae4fce1f69",
+    mediaId: "7233c32db0bb434c826b1cb6972dbdf2",
+    position: 1,
+    media: {
+      translated: {
+        alt: null,
+        title: null,
+        customFields: {},
+      },
+      createdAt: "2020-08-06T06:25:57.072+00:00",
+      updatedAt: "2020-08-06T06:26:45.789+00:00",
+      mimeType: "image/jpeg",
+      fileExtension: "jpg",
+      fileSize: 320388,
+      title: null,
+      metaData: {
+        type: 2,
+        width: 1380,
+        height: 3588,
+      },
+      uploadedAt: null,
+      alt: null,
+      url: "https://cdn.shopware.store/a/B/m/pPkDE/media/a4/65/72/Cigar_Special.jpg",
+      fileName: "Cigar_Special",
+      translations: null,
+      thumbnails: [
+        {
+          translated: [],
+          createdAt: "2020-08-06T06:25:57.070+00:00",
+          updatedAt: "2020-08-06T06:26:45.787+00:00",
+          width: 800,
+          height: 800,
+          url: "https://cdn.shopware.store/a/B/m/pPkDE/thumbnail/a4/65/72/Cigar_Special_800x800.jpg",
+          mediaId: "7233c32db0bb434c826b1cb6972dbdf2",
+          customFields: null,
+          id: "2b039146738b400995c67e4bfdb63aa9",
+          apiAlias: "media_thumbnail",
+        },
+        {
+          translated: [],
+          createdAt: "2020-08-06T06:25:57.071+00:00",
+          updatedAt: "2020-08-06T06:26:45.788+00:00",
+          width: 400,
+          height: 400,
+          url: "https://cdn.shopware.store/a/B/m/pPkDE/thumbnail/a4/65/72/Cigar_Special_400x400.jpg",
+          mediaId: "7233c32db0bb434c826b1cb6972dbdf2",
+          customFields: null,
+          id: "89e03f87e1f54a499e3a4bfc231974ee",
+          apiAlias: "media_thumbnail",
+        },
+        {
+          translated: [],
+          createdAt: "2020-08-06T06:25:57.072+00:00",
+          updatedAt: "2020-08-06T06:26:45.789+00:00",
+          width: 1920,
+          height: 1920,
+          url: "https://cdn.shopware.store/a/B/m/pPkDE/thumbnail/a4/65/72/Cigar_Special_1920x1920.jpg",
+          mediaId: "7233c32db0bb434c826b1cb6972dbdf2",
+          customFields: null,
+          id: "fd29b1641cc14e4fb40d336e910c69c3",
+          apiAlias: "media_thumbnail",
+        },
+      ],
+      hasFile: true,
+      private: false,
+      customFields: null,
+      id: "7233c32db0bb434c826b1cb6972dbdf2",
+      apiAlias: "media",
+    },
+    customFields: null,
+    id: "f18e3715c27746b9afab3dd28fd7daf3",
+    apiAlias: "product_media",
+  },
+  parent: null,
+  children: null,
+  media: [
+    {
+      versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",
+      translated: [],
+      createdAt: "2020-08-06T06:26:45.767+00:00",
+      updatedAt: "2023-06-26T20:20:14.427+00:00",
+      productId: "7e117859f69b4ee08947e1ae4fce1f69",
+      mediaId: "c3c3a2f9a92a4d0cbcf682f9b17f29f4",
+      position: 0,
+      media: {
+        translated: {
+          alt: null,
+          title: null,
+          customFields: {},
+        },
+        createdAt: "2020-08-06T06:26:02.517+00:00",
+        updatedAt: "2020-08-06T06:26:45.784+00:00",
+        mimeType: "image/jpeg",
+        fileExtension: "jpg",
+        fileSize: 320388,
+        title: null,
+        metaData: {
+          type: 2,
+          width: 1380,
+          height: 3588,
+        },
+        uploadedAt: null,
+        alt: null,
+        url: "https://cdn.shopware.store/a/B/m/pPkDE/media/4b/7b/ee/SW10044.jpg",
+        fileName: "SW10044",
+        translations: null,
+        thumbnails: [
+          {
+            translated: [],
+            createdAt: "2020-08-06T06:26:02.515+00:00",
+            updatedAt: "2020-08-06T06:26:45.782+00:00",
+            width: 800,
+            height: 800,
+            url: "https://cdn.shopware.store/a/B/m/pPkDE/thumbnail/4b/7b/ee/SW10044_800x800.jpg",
+            mediaId: "c3c3a2f9a92a4d0cbcf682f9b17f29f4",
+            customFields: null,
+            id: "88c1bbadc4094e84be02eed9f4dd9b21",
+            apiAlias: "media_thumbnail",
+          },
+          {
+            translated: [],
+            createdAt: "2020-08-06T06:26:02.516+00:00",
+            updatedAt: "2020-08-06T06:26:45.783+00:00",
+            width: 1920,
+            height: 1920,
+            url: "https://cdn.shopware.store/a/B/m/pPkDE/thumbnail/4b/7b/ee/SW10044_1920x1920.jpg",
+            mediaId: "c3c3a2f9a92a4d0cbcf682f9b17f29f4",
+            customFields: null,
+            id: "b4e007f7a9ff4180858dcd3eda74511f",
+            apiAlias: "media_thumbnail",
+          },
+          {
+            translated: [],
+            createdAt: "2020-08-06T06:26:02.517+00:00",
+            updatedAt: "2020-08-06T06:26:45.783+00:00",
+            width: 400,
+            height: 400,
+            url: "https://cdn.shopware.store/a/B/m/pPkDE/thumbnail/4b/7b/ee/SW10044_400x400.jpg",
+            mediaId: "c3c3a2f9a92a4d0cbcf682f9b17f29f4",
+            customFields: null,
+            id: "e9f8287bb01d4024889a6632bdc55533",
+            apiAlias: "media_thumbnail",
+          },
+        ],
+        hasFile: true,
+        private: false,
+        customFields: null,
+        id: "c3c3a2f9a92a4d0cbcf682f9b17f29f4",
+        apiAlias: "media",
+      },
+      customFields: null,
+      id: "57e282b46fa24848a5da0dc42ad43868",
+      apiAlias: "product_media",
+    },
+    {
+      versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",
+      translated: [],
+      createdAt: "2020-08-06T06:26:45.773+00:00",
+      updatedAt: "2023-06-26T20:20:14.427+00:00",
+      productId: "7e117859f69b4ee08947e1ae4fce1f69",
+      mediaId: "7233c32db0bb434c826b1cb6972dbdf2",
+      position: 1,
+      media: {
+        translated: {
+          alt: null,
+          title: null,
+          customFields: {},
+        },
+        createdAt: "2020-08-06T06:25:57.072+00:00",
+        updatedAt: "2020-08-06T06:26:45.789+00:00",
+        mimeType: "image/jpeg",
+        fileExtension: "jpg",
+        fileSize: 320388,
+        title: null,
+        metaData: {
+          type: 2,
+          width: 1380,
+          height: 3588,
+        },
+        uploadedAt: null,
+        alt: null,
+        url: "https://cdn.shopware.store/a/B/m/pPkDE/media/a4/65/72/Cigar_Special.jpg",
+        fileName: "Cigar_Special",
+        translations: null,
+        thumbnails: [
+          {
+            translated: [],
+            createdAt: "2020-08-06T06:25:57.070+00:00",
+            updatedAt: "2020-08-06T06:26:45.787+00:00",
+            width: 800,
+            height: 800,
+            url: "https://cdn.shopware.store/a/B/m/pPkDE/thumbnail/a4/65/72/Cigar_Special_800x800.jpg",
+            mediaId: "7233c32db0bb434c826b1cb6972dbdf2",
+            customFields: null,
+            id: "2b039146738b400995c67e4bfdb63aa9",
+            apiAlias: "media_thumbnail",
+          },
+          {
+            translated: [],
+            createdAt: "2020-08-06T06:25:57.071+00:00",
+            updatedAt: "2020-08-06T06:26:45.788+00:00",
+            width: 400,
+            height: 400,
+            url: "https://cdn.shopware.store/a/B/m/pPkDE/thumbnail/a4/65/72/Cigar_Special_400x400.jpg",
+            mediaId: "7233c32db0bb434c826b1cb6972dbdf2",
+            customFields: null,
+            id: "89e03f87e1f54a499e3a4bfc231974ee",
+            apiAlias: "media_thumbnail",
+          },
+          {
+            translated: [],
+            createdAt: "2020-08-06T06:25:57.072+00:00",
+            updatedAt: "2020-08-06T06:26:45.789+00:00",
+            width: 1920,
+            height: 1920,
+            url: "https://cdn.shopware.store/a/B/m/pPkDE/thumbnail/a4/65/72/Cigar_Special_1920x1920.jpg",
+            mediaId: "7233c32db0bb434c826b1cb6972dbdf2",
+            customFields: null,
+            id: "fd29b1641cc14e4fb40d336e910c69c3",
+            apiAlias: "media_thumbnail",
+          },
+        ],
+        hasFile: true,
+        private: false,
+        customFields: null,
+        id: "7233c32db0bb434c826b1cb6972dbdf2",
+        apiAlias: "media",
+      },
+      customFields: null,
+      id: "f18e3715c27746b9afab3dd28fd7daf3",
+      apiAlias: "product_media",
+    },
+  ],
+  cmsPageId: null,
+  cmsPage: null,
+  translations: null,
+  categories: null,
+  properties: null,
+  options: null,
+  configuratorSettings: null,
+  categoriesRo: null,
+  coverId: "f18e3715c27746b9afab3dd28fd7daf3",
+  categoryIds: ["2b91ba3b1b8d40658eda14f62450152a"],
+  productReviews: null,
+  ratingAverage: null,
+  mainCategories: null,
+  seoUrls: [
+    {
+      translated: [],
+      createdAt: "2020-08-06T06:26:52.576+00:00",
+      updatedAt: "2022-08-03T08:07:04.512+00:00",
+      salesChannelId: "98432def39fc4624b33213a56b8c944d",
+      languageId: "2fbb5fe2e29a4d70aa5854ce7ce3e20b",
+      routeName: "frontend.detail.page",
+      foreignKey: "7e117859f69b4ee08947e1ae4fce1f69",
+      pathInfo: "/detail/7e117859f69b4ee08947e1ae4fce1f69",
+      seoPathInfo: "Cigar-Special-40/SW10044",
+      isCanonical: true,
+      isModified: false,
+      isDeleted: false,
+      isValid: null,
+      url: null,
+      customFields: null,
+      id: "dd38c5ed443544e28818e967809c8914",
+      apiAlias: "seo_url",
+    },
+  ],
+  crossSellings: null,
+  canonicalProductId: null,
+  canonicalProduct: null,
+  streams: null,
+  downloads: null,
+  states: ["is-physical"],
+  customFields: {},
+  id: "7e117859f69b4ee08947e1ae4fce1f69",
+  calculatedPrices: [],
+  calculatedPrice: {
+    unitPrice: 14.45,
+    quantity: 1,
+    totalPrice: 14.45,
+    calculatedTaxes: [
+      {
+        tax: 0.95,
+        taxRate: 7,
+        price: 14.45,
+        apiAlias: "cart_tax_calculated",
+      },
+    ],
+    taxRules: [
+      {
+        taxRate: 7,
+        percentage: 100,
+        apiAlias: "cart_tax_rule",
+      },
+    ],
+    referencePrice: {
+      purchaseUnit: 0.2,
+      referenceUnit: 1,
+      unitName: "litre",
+      price: 72.25,
+      apiAlias: "cart_price_reference",
+    },
+    listPrice: null,
+    regulationPrice: null,
+    apiAlias: "calculated_price",
+  },
+  sortedProperties: null,
+  calculatedCheapestPrice: {
+    unitPrice: 14.45,
+    quantity: 1,
+    totalPrice: 14.45,
+    calculatedTaxes: [
+      {
+        tax: 0.95,
+        taxRate: 7,
+        price: 14.45,
+        apiAlias: "cart_tax_calculated",
+      },
+    ],
+    taxRules: [
+      {
+        taxRate: 7,
+        percentage: 100,
+        apiAlias: "cart_tax_rule",
+      },
+    ],
+    referencePrice: {
+      purchaseUnit: 0.2,
+      referenceUnit: 1,
+      unitName: "litre",
+      price: 72.25,
+      apiAlias: "cart_price_reference",
+    },
+    listPrice: null,
+    regulationPrice: null,
+    hasRange: true,
+    variantId: "7e117859f69b4ee08947e1ae4fce1f69",
+    apiAlias: "calculated_cheapest_price",
+  },
+  isNew: false,
+  calculatedMaxPurchase: 100,
+  //   seoCategory: {
+  //     // extensions: {
+  //     //   search: {
+  //     //     extensions: [],
+  //     //     _uniqueIdentifier: null,
+  //     //     translated: [],
+  //     //     autoIncrement: 47,
+  //     //   },
+  //     // },
+  //     versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",
+  //     translated: {
+  //       breadcrumb: [
+  //         "Home",
+  //         "Welcome to Shopware Frontends!",
+  //         "Cooking pleasure \u0026 Provence",
+  //         "Drink",
+  //         "Brandies",
+  //       ],
+  //       name: "Brandies",
+  //       customFields: {},
+  //       linkType: null,
+  //       internalLink: null,
+  //       externalLink: null,
+  //       linkNewTab: null,
+  //       description: null,
+  //       metaTitle: null,
+  //       metaDescription: null,
+  //       keywords: null,
+  //     },
+  //     createdAt: "2020-08-06T06:26:25.406+00:00",
+  //     updatedAt: null,
+  //     afterCategoryId: null,
+  //     parentId: "54a3e428361147c0a3372d44070fb84d",
+  //     mediaId: null,
+  //     name: "Brandies",
+  //     breadcrumb: [
+  //       "Home",
+  //       "Welcome to Shopware Frontends!",
+  //       "Cooking pleasure \u0026 Provence",
+  //       "Drink",
+  //       "Brandies",
+  //     ],
+  //     path: "|525abe8981214bd2ba94fd33942333ec|bda4b60e845240b2b9d6b60e71196e14|ca15f1e906204da49afe666bbcca7825|54a3e428361147c0a3372d44070fb84d|",
+  //     level: 5,
+  //     active: true,
+  //     childCount: 0,
+  //     visibleChildCount: 0,
+  //     displayNestedProducts: true,
+  //     parent: null,
+  //     children: null,
+  //     translations: null,
+  //     media: null,
+  //     cmsPageId: "33043b9b89d642eb93cc8d14b8cf8f14",
+  //     cmsPageIdSwitched: false,
+  //     cmsPage: null,
+  //     linkType: null,
+  //     linkNewTab: null,
+  //     internalLink: null,
+  //     externalLink: null,
+  //     visible: true,
+  //     type: "page",
+  //     productAssignmentType: "product",
+  //     description: null,
+  //     metaTitle: null,
+  //     metaDescription: null,
+  //     keywords: null,
+  //     seoUrls: [
+  //       {
+  //         translated: [],
+  //         createdAt: "2022-08-01T14:02:03.482+00:00",
+  //         updatedAt: "2022-08-08T10:23:53.514+00:00",
+  //         salesChannelId: "98432def39fc4624b33213a56b8c944d",
+  //         languageId: "2fbb5fe2e29a4d70aa5854ce7ce3e20b",
+  //         routeName: "frontend.navigation.page",
+  //         foreignKey: "2b91ba3b1b8d40658eda14f62450152a",
+  //         pathInfo: "/navigation/2b91ba3b1b8d40658eda14f62450152a",
+  //         seoPathInfo: "Cooking-pleasure-Provence/Drink/Brandies/",
+  //         isCanonical: true,
+  //         isModified: false,
+  //         isDeleted: false,
+  //         isValid: null,
+  //         url: null,
+  //         customFields: null,
+  //         id: "9f13d27e18d84ff1ae7b1f01f9db9e63",
+  //         apiAlias: "seo_url",
+  //       },
+  //     ],
+  //     customEntityTypeId: null,
+  //     customFields: null,
+  //     id: "2b91ba3b1b8d40658eda14f62450152a",
+  //     apiAlias: "category",
+  //   },
+};
+
+export default product;
diff --git a/packages/composables/src/mocks/Salutations.ts b/packages/composables/src/mocks/Salutations.ts
new file mode 100644
index 00000000..83f5e534
--- /dev/null
+++ b/packages/composables/src/mocks/Salutations.ts
@@ -0,0 +1,51 @@
+import { Salutation } from "@shopware-pwa/types";
+
+const salutations: Salutation[] = [
+  {
+    translated: {
+      displayName: "Mr.",
+      letterName: "Dear Mr.",
+      customFields: {},
+    },
+    createdAt: "2020-08-06T06:23:29.757+00:00",
+    updatedAt: "2020-08-06T06:26:29.283+00:00",
+    salutationKey: "mr",
+    displayName: "Mr.",
+    letterName: "Dear Mr.",
+    customFields: null,
+    id: "d5e543063dd642b48ef94b02d68e5785",
+    apiAlias: "salutation",
+  },
+  {
+    translated: {
+      displayName: "Mrs.",
+      letterName: "Dear Mrs.",
+      customFields: {},
+    },
+    createdAt: "2020-08-06T06:23:29.768+00:00",
+    updatedAt: "2020-08-06T06:26:29.267+00:00",
+    salutationKey: "mrs",
+    displayName: "Mrs.",
+    letterName: "Dear Mrs.",
+    customFields: null,
+    id: "fef89b093c4843af86f26a07d0e81108",
+    apiAlias: "salutation",
+  },
+  {
+    translated: {
+      displayName: "Not specified",
+      letterName: " ",
+      customFields: {},
+    },
+    createdAt: "2020-08-06T06:23:29.780+00:00",
+    updatedAt: "2020-08-06T06:26:29.274+00:00",
+    salutationKey: "not_specified",
+    displayName: "Not specified",
+    letterName: " ",
+    customFields: null,
+    id: "b84038d62c3f4d66b3bc626df7fa949e",
+    apiAlias: "salutation",
+  },
+];
+
+export default salutations;
diff --git a/packages/composables/src/useNavigation.spec.ts b/packages/composables/src/useNavigation.spec.ts
new file mode 100644
index 00000000..ba059ef2
--- /dev/null
+++ b/packages/composables/src/useNavigation.spec.ts
@@ -0,0 +1,53 @@
+import { describe, expect, it, vi } from "vitest";
+import { shallowMount } from "@vue/test-utils";
+import { useNavigation } from "./useNavigation";
+import * as apiExports from "@shopware-pwa/api-client";
+import Menu from "./mocks/Menu";
+
+const getMockProvide = () => ({
+  global: {
+    provide: {
+      shopware: {
+        apiInstance: {
+          config: {},
+        },
+      },
+    },
+  },
+});
+
+const Component = {
+  template: "<div/>",
+  props: {},
+  setup() {
+    const { navigationElements, loadNavigationElements } = useNavigation();
+    return { navigationElements, loadNavigationElements };
+  },
+};
+
+describe("useNavigation", () => {
+  const wrapper = shallowMount(Component, getMockProvide());
+
+  vi.spyOn(apiExports, "getStoreNavigation").mockImplementation(() => {
+    return new Promise((resolve) => {
+      resolve(Menu);
+    });
+  });
+
+  it("should set the menu", async () => {
+    await wrapper.vm.loadNavigationElements({
+      depth: 3,
+    });
+    expect(wrapper.vm.navigationElements.length).toBe(Menu.length);
+  });
+
+  it("menu is empty because of the error", async () => {
+    vi.spyOn(apiExports, "getStoreNavigation").mockImplementation(() => {
+      throw new Error("Test error");
+    });
+    await wrapper.vm.loadNavigationElements({
+      depth: 3,
+    });
+    expect(wrapper.vm.navigationElements.length).toBe(0);
+  });
+});
diff --git a/packages/composables/src/useProduct.spec.ts b/packages/composables/src/useProduct.spec.ts
new file mode 100644
index 00000000..0a2926fc
--- /dev/null
+++ b/packages/composables/src/useProduct.spec.ts
@@ -0,0 +1,24 @@
+import { describe, expect, it } from "vitest";
+import { shallowMount } from "@vue/test-utils";
+import { useProduct } from "./useProduct";
+import mockedProduct from "./mocks/Product";
+const Component = {
+  template: "<div/>",
+  props: {},
+  setup() {
+    const { product, configurator, changeVariant } = useProduct(mockedProduct);
+    return { product, configurator, changeVariant };
+  },
+};
+
+describe("useProduct", () => {
+  const wrapper = shallowMount(Component);
+
+  it("should return product object", () => {
+    expect(wrapper.vm.product).toStrictEqual(mockedProduct);
+  });
+
+  it("should return undefined configurator object", () => {
+    expect(wrapper.vm.configurator).toBe(undefined);
+  });
+});
diff --git a/packages/composables/src/useProduct.ts b/packages/composables/src/useProduct.ts
index c1a5dc42..a02c9a24 100644
--- a/packages/composables/src/useProduct.ts
+++ b/packages/composables/src/useProduct.ts
@@ -5,6 +5,7 @@ import ContextError from "./helpers/ContextError";
 
 export type UseProductReturn = {
   /**
+   * Returns product object
    * {@link Product} object
    */
   product: ComputedRef<Product>;
diff --git a/packages/composables/src/useSalutations.spec.ts b/packages/composables/src/useSalutations.spec.ts
new file mode 100644
index 00000000..7c70240f
--- /dev/null
+++ b/packages/composables/src/useSalutations.spec.ts
@@ -0,0 +1,47 @@
+import { useSalutations } from "./useSalutations";
+import { describe, expect, it, vi } from "vitest";
+import { shallowMount } from "@vue/test-utils";
+import Salutations from "./mocks/Salutations";
+import * as apiExports from "@shopware-pwa/api-client";
+
+const Component = {
+  template: "<div/>",
+  props: {},
+  setup() {
+    const { mountedCallback, fetchSalutations, getSalutations } =
+      useSalutations();
+
+    return {
+      mountedCallback,
+      fetchSalutations,
+      getSalutations,
+    };
+  },
+};
+
+const getMockProvide = () => ({
+  global: {
+    provide: {
+      shopware: {
+        apiInstance: {
+          config: {},
+        },
+      },
+    },
+  },
+});
+
+describe("useSalutations", () => {
+  vi.spyOn(apiExports, "getAvailableSalutations").mockImplementation(() => {
+    return new Promise((resolve) => {
+      resolve({ elements: Salutations });
+    });
+  });
+
+  const wrapper = shallowMount(Component, getMockProvide());
+
+  it("should init value on init", async () => {
+    await wrapper.vm.mountedCallback();
+    expect(wrapper.vm.getSalutations).toStrictEqual(Salutations);
+  });
+});
diff --git a/packages/composables/src/useUser.spec.ts b/packages/composables/src/useUser.spec.ts
new file mode 100644
index 00000000..a2412fc5
--- /dev/null
+++ b/packages/composables/src/useUser.spec.ts
@@ -0,0 +1,102 @@
+import { useUser } from "./useUser";
+import * as useSessionContext from "./useSessionContext";
+import * as useCart from "./useCart";
+import { describe, expect, it, vi } from "vitest";
+import { shallowMount } from "@vue/test-utils";
+import * as apiExports from "@shopware-pwa/api-client";
+import LoginResponse from "./mocks/LoginResponse";
+
+const Component = {
+  template: "<div/>",
+  props: {},
+  setup() {
+    const {
+      login,
+      register,
+      user,
+      isLoggedIn,
+      isCustomerSession,
+      isGuestSession,
+      refreshUser,
+      logout,
+      updateEmail,
+      updatePersonalInfo,
+      setDefaultPaymentMethod,
+      loadSalutation,
+      salutation,
+      loadCountry,
+      country,
+      defaultBillingAddressId,
+      defaultShippingAddressId,
+      userDefaultPaymentMethod,
+      userDefaultBillingAddress,
+      userDefaultShippingAddress,
+    } = useUser();
+
+    return {
+      login,
+      register,
+      user,
+      isLoggedIn,
+      isCustomerSession,
+      isGuestSession,
+      refreshUser,
+      logout,
+      updateEmail,
+      updatePersonalInfo,
+      setDefaultPaymentMethod,
+      loadSalutation,
+      salutation,
+      loadCountry,
+      country,
+      defaultBillingAddressId,
+      defaultShippingAddressId,
+      userDefaultPaymentMethod,
+      userDefaultBillingAddress,
+      userDefaultShippingAddress,
+    };
+  },
+};
+
+const getMockProvide = () => ({
+  global: {
+    provide: {
+      shopware: {
+        apiInstance: {
+          config: {},
+        },
+      },
+    },
+  },
+});
+
+describe("useUser", () => {
+  vi.spyOn(apiExports, "login").mockImplementation(() => {
+    return new Promise((resolve) => {
+      resolve(LoginResponse);
+    });
+  });
+
+  const useSessionContextSpy = vi
+    .spyOn(useSessionContext, "useSessionContext")
+    .mockImplementation((): any => {
+      return {
+        refreshSessionContext: () => undefined,
+      };
+    });
+  const useCartSpy = vi
+    .spyOn(useCart, "useCart")
+    .mockImplementation((): any => {
+      return {
+        refreshCart: () => undefined,
+      };
+    });
+
+  const wrapper = shallowMount(Component, getMockProvide());
+  it("login function", async () => {
+    await wrapper.vm.login("test@test.zzz", "test");
+
+    expect(useSessionContextSpy).toHaveBeenCalled();
+    expect(useCartSpy).toHaveBeenCalled();
+  });
+});
diff --git a/packages/types/shopware-6-client/models/checkout/cart/price/CalculatedPrice.d.ts b/packages/types/shopware-6-client/models/checkout/cart/price/CalculatedPrice.d.ts
index 6f813ab8..e30319e8 100644
--- a/packages/types/shopware-6-client/models/checkout/cart/price/CalculatedPrice.d.ts
+++ b/packages/types/shopware-6-client/models/checkout/cart/price/CalculatedPrice.d.ts
@@ -12,15 +12,16 @@ export type CalculatedPrice = {
   calculatedTaxes: CalculatedTax[];
   taxRules: TaxRule[];
   referencePrice: ReferencePrice;
-  hasRange: boolean;
+  hasRange?: boolean;
   listPrice: {
     price: number;
     discount: number;
     percentage: number;
     apiAlias: string;
-  };
+  } | null;
   regulationPrice: null | {
     price: number;
   };
-  apiAlias: "calculated_price";
+  apiAlias: string;
+  variantId?: string;
 };
diff --git a/packages/types/shopware-6-client/models/checkout/cart/price/ReferencePrice.d.ts b/packages/types/shopware-6-client/models/checkout/cart/price/ReferencePrice.d.ts
index 4ad4eee0..4ad2c46d 100644
--- a/packages/types/shopware-6-client/models/checkout/cart/price/ReferencePrice.d.ts
+++ b/packages/types/shopware-6-client/models/checkout/cart/price/ReferencePrice.d.ts
@@ -1,5 +1,7 @@
 export type ReferencePrice = {
   price: number;
-  referenceUnit: string;
+  purchaseUnit: number;
+  referenceUnit: number | string;
   unitName: string;
+  apiAlias?: string;
 };
diff --git a/packages/types/shopware-6-client/models/common/Entity.d.ts b/packages/types/shopware-6-client/models/common/Entity.d.ts
index 1f5ff270..b9f22824 100644
--- a/packages/types/shopware-6-client/models/common/Entity.d.ts
+++ b/packages/types/shopware-6-client/models/common/Entity.d.ts
@@ -7,10 +7,10 @@
  * @beta
  */
 export type Entity = {
-  _uniqueIdentifier: string;
+  _uniqueIdentifier?: string;
   versionId: string;
   translated: unknown;
-  createdAt: Date | null;
+  createdAt: Date | string | null;
   updatedAt: Date | string | null;
   apiAlias: string;
 };
diff --git a/packages/types/shopware-6-client/models/content/category/Category.d.ts b/packages/types/shopware-6-client/models/content/category/Category.d.ts
index 5eb1e6c4..9757471a 100644
--- a/packages/types/shopware-6-client/models/content/category/Category.d.ts
+++ b/packages/types/shopware-6-client/models/content/category/Category.d.ts
@@ -5,7 +5,7 @@ import { Entity } from "../../common/Entity";
 import { Tag } from "../../system/tag/Tag";
 import { CategoryTranslation } from "./CategoryTranslation";
 import { CustomFields } from "../../common/CustomField";
-
+import { SeoUrl } from "../navigation/Navigation";
 /**
  * Source: https://github.com/shopware/platform/blob/master/src/Core/Content/Category/CategoryDefinition.php#L50
  *
@@ -18,7 +18,7 @@ export type CategoryType = "page" | "link" | "folder";
  */
 export type Category = Entity & {
   parentId: string | null;
-  autoIncrement: number;
+  autoIncrement?: number;
   mediaId: string | null;
   name: string | null;
   breadcrumb: string[];
@@ -30,45 +30,49 @@ export type Category = Entity & {
   children: Category[] | null;
   translations: CategoryTranslation[] | null;
   media: Media | null;
-  products: Product[] | null;
-  nestedProducts: Product[] | null;
+  products?: Product[] | null;
+  nestedProducts?: Product[] | null;
   afterCategoryId: string | null;
-  customFields: CustomFields;
-  tags: Tag[] | null;
+  customFields: CustomFields | null;
+  tags?: Tag[] | null;
   cmsPageId: string | null;
   cmsPage: CmsPage | null;
-  slotConfig: [] | null;
+  slotConfig?: [] | null;
   externalLink: string | null;
-  linkNewTab: boolean;
+  linkNewTab: boolean | null;
   visible: boolean;
   type: CategoryType;
-  description: string;
+  description: string | null;
   id: string;
-  parentVersionId: string;
-  childrenCount: number;
-  afterCategoryVersionId: string;
+  parentVersionId?: string;
+  childrenCount?: number;
+  afterCategoryVersionId?: string;
   path?: string;
   route?: {
     path?: string;
   };
-  seoUrls: {
-    apiAlias: string;
-    pathInfo: string;
-    seoPathInfo: string;
-  }[];
+  seoUrls: SeoUrl[] | null;
   metaTitle: string | null;
   metaDescription: string | null;
   keywords: string | null;
   translated: {
     name: string;
     breadcrumb: string[];
-    description: string;
-    externalLink: string;
+    description: string | null;
+    externalLink: string | null;
     metaTitle: string | null;
     metaDescription: string | null;
     keywords: string | null;
-    customFields: CustomFields;
+    customFields: CustomFields | null;
+    linkType: string | null;
+    internalLink: string | null;
+    linkNewTab: string | null;
   };
-  linkType: "product" | "category" | "landing_page" | string;
+  linkType: "product" | "category" | "landing_page" | string | null;
   internalLink: string | null;
+  extensions?: unknown;
+  visibleChildCount?: number | null;
+  cmsPageIdSwitched?: boolean;
+  productAssignmentType?: string;
+  customEntityTypeId?: string | null;
 };
diff --git a/packages/types/shopware-6-client/models/content/media/Media.d.ts b/packages/types/shopware-6-client/models/content/media/Media.d.ts
index 96ef1a8f..52ec2c81 100644
--- a/packages/types/shopware-6-client/models/content/media/Media.d.ts
+++ b/packages/types/shopware-6-client/models/content/media/Media.d.ts
@@ -26,12 +26,12 @@ export type Media = {
   fileSize: number;
   title: null | string;
   metaData: {
-    hash: string;
+    hash?: string;
     type: number;
     width: number;
     height: number;
   };
-  uploadedAt: string;
+  uploadedAt: Date | string | null;
   alt: null | string;
   url: string;
   fileName: string;
@@ -39,17 +39,17 @@ export type Media = {
   thumbnails: MediaThumbnail[];
   hasFile: boolean;
   private: boolean;
-  _uniqueIdentifier: string;
-  versionId: null | string;
+  _uniqueIdentifier?: string;
+  versionId?: null | string;
   translated: {
     alt: null | string;
     title: null | string;
-    customFields: CustomFields;
+    customFields: CustomFields | null;
   };
   createdAt: string;
   updatedAt: Date | string | null;
-  extensions: unknown;
+  extensions?: unknown;
   id: string;
-  customFields: CustomFields;
+  customFields: CustomFields | null;
   apiAlias: "media";
 };
diff --git a/packages/types/shopware-6-client/models/content/media/MediaThumbnail.d.ts b/packages/types/shopware-6-client/models/content/media/MediaThumbnail.d.ts
index b759d774..83824644 100644
--- a/packages/types/shopware-6-client/models/content/media/MediaThumbnail.d.ts
+++ b/packages/types/shopware-6-client/models/content/media/MediaThumbnail.d.ts
@@ -5,13 +5,13 @@ export type MediaThumbnail = {
   height: number;
   url: string;
   mediaId: string;
-  _uniqueIdentifier: string;
-  versionId: null | string;
+  _uniqueIdentifier?: string;
+  versionId?: null | string;
   translated: unknown;
   createdAt: string;
   updatedAt: Date | string | null;
-  extensions: unknown;
+  extensions?: unknown;
   id: string;
-  customFields: CustomFields;
+  customFields: CustomFields | null;
   apiAlias: "media_thumbnail";
 };
diff --git a/packages/types/shopware-6-client/models/content/navigation/Navigation.d.ts b/packages/types/shopware-6-client/models/content/navigation/Navigation.d.ts
index 77a195b9..8e0bebd1 100644
--- a/packages/types/shopware-6-client/models/content/navigation/Navigation.d.ts
+++ b/packages/types/shopware-6-client/models/content/navigation/Navigation.d.ts
@@ -33,19 +33,19 @@ export type SeoUrl = {
    * @deprecated removed from 6.5.0
    */
   isValid: null | boolean;
-  language: null | boolean;
+  language?: null | boolean;
   url: null | string;
   customFields: CustomFields | null;
   /**
    * @deprecated removed from 6.5.0
    */
-  error: null | any;
-  _uniqueIdentifier: string;
-  versionId: null | string;
+  error?: null | any;
+  _uniqueIdentifier?: string;
+  versionId?: null | string;
   translated: [];
-  createdAt: Date;
+  createdAt: Date | string;
   updatedAt: Date | string | null;
-  extensions: unknown;
+  extensions?: unknown;
   id: string;
   apiAlias: string;
 };
diff --git a/packages/types/shopware-6-client/models/content/product/Product.d.ts b/packages/types/shopware-6-client/models/content/product/Product.d.ts
index 5776f5c6..0617e3f2 100644
--- a/packages/types/shopware-6-client/models/content/product/Product.d.ts
+++ b/packages/types/shopware-6-client/models/content/product/Product.d.ts
@@ -59,27 +59,28 @@ export type Product = {
   available: boolean;
   availableStock: number | null;
   calculatedCheapestPrice: CalculatedPrice;
-  calculatedListingPrice: ListingPrice;
+  calculatedListingPrice: ListingPrice | null;
   calculatedMaxPurchase: number;
   calculatedPrice: CalculatedPrice;
   calculatedPrices: CalculatedPrice[];
   canonicalProduct: any;
   canonicalProductId: string | null;
-  categories: Category[];
+  categories: Category[] | null;
   categoriesRo: Category[] | null;
   categoryIds: string[];
-  categoryTree: [] | null;
-  cheapestPrice: ProductCheapestPrice;
-  childCount: number;
-  children: Product[];
+  categoryTree: string[] | null;
+  cheapestPrice: ProductCheapestPrice | null;
+  childCount: number | null;
+  children: Product[] | null;
   cmsPage: null | CmsPage;
   cmsPageId: string | null;
   configuratorSettings: PropertyGroup[] | null;
   cover: ProductMedia;
   coverId: string | null;
-  createdAt: Date;
-  crossSellings: CrossSelling[];
+  createdAt: Date | string;
+  crossSellings: CrossSelling[] | null;
   customFields: CustomFields;
+  updatedAt: Date | string | null;
   deliveryTime: DeliveryTime;
   deliveryTimeId: string | null;
   description: string | null;
@@ -94,7 +95,7 @@ export type Product = {
   keywords: string | null;
   length: number | null;
   listingPrices: ListingPrice[] | null;
-  mainCategories: ProductMainCategories;
+  mainCategories: ProductMainCategories | null;
   manufacturer: ProductManufacturer | null;
   manufacturerId: string | null;
   manufacturerNumber: string | null;
@@ -106,15 +107,15 @@ export type Product = {
   minPurchase: number | null;
   name: string | null;
   optionIds?: string[];
-  options: PropertyGroupOption[];
+  options: PropertyGroupOption[] | null;
   packUnit: string | null;
   packUnitPlural: string | null;
-  parent: Product;
+  parent: Product | null;
   parentId: string | null;
-  parentVersionId: string;
+  parentVersionId: string | null;
   price: Price[] | null;
-  prices: ProductPrice[];
-  productManufacturerVersionId: string;
+  prices: ProductPrice[] | null;
+  productManufacturerVersionId: string | null;
   productNumber: string;
   productReviews: ProductReview[] | null;
   properties: PropertyGroupOption[] | null;
@@ -124,23 +125,23 @@ export type Product = {
   purchaseUnit: number | null;
   ratingAverage: number | null;
   referenceUnit: number | null;
-  releaseDate: Date;
+  releaseDate: Date | string | null;
   restockTime: number;
   sales: number;
-  seoCategory: Category;
+  seoCategory: Category | null;
   seoUrls: SeoUrl[] | null;
   shippingFree: boolean | null;
   sortedProperties: PropertyGroup[] | null;
   states: string[];
   stock: number;
   streamIds: string[] | null;
-  streams: ProductStreams;
+  streams: ProductStreams | null;
   tagIds: [] | null;
-  tags: Tag[];
+  tags: Tag[] | null;
   tax: Tax;
   taxId: string | null;
   translated: ProductTranslated;
-  translations: ProductTranslation[];
+  translations: ProductTranslation[] | null;
   unit: Unit | null;
   unitId: string | null;
   updatedAt: Date;
@@ -172,6 +173,8 @@ export type ProductTranslated = {
   keywords: string | null;
   metaTitle: string | null;
   customFields: CustomFields;
+  packUnit: string | null;
+  packUnitPlural: string | null;
 };
 
 export type ProductMainCategories = {
diff --git a/packages/types/shopware-6-client/models/content/product/ProductMedia.d.ts b/packages/types/shopware-6-client/models/content/product/ProductMedia.d.ts
index a722f42b..4c8007dc 100644
--- a/packages/types/shopware-6-client/models/content/product/ProductMedia.d.ts
+++ b/packages/types/shopware-6-client/models/content/product/ProductMedia.d.ts
@@ -11,12 +11,12 @@ export type ProductMedia = {
   position: number;
   media: Media;
   customFields: null | CustomFields;
-  _uniqueIdentifier: string;
+  _uniqueIdentifier?: string;
   versionId: string;
   translated: [];
   createdAt: string;
   updatedAt: Date | string | null;
-  extensions: unknown;
+  extensions?: unknown;
   id: string;
   apiAlias: "product_media";
 };
diff --git a/packages/types/shopware-6-client/models/system/salutation/Salutation.d.ts b/packages/types/shopware-6-client/models/system/salutation/Salutation.d.ts
index bd3ddbc3..ea068f55 100644
--- a/packages/types/shopware-6-client/models/system/salutation/Salutation.d.ts
+++ b/packages/types/shopware-6-client/models/system/salutation/Salutation.d.ts
@@ -1,5 +1,5 @@
 import { SalutationTranslation } from "./SalutationTranslation";
-
+import { CustomFields } from "../models/common/CustomField";
 /**
  * @public
  */
@@ -9,8 +9,10 @@ export type Salutation = {
   displayName: string | null;
   letterName: string | null;
   createdAt: string;
-  translations: SalutationTranslation[] | null;
+  translations?: SalutationTranslation[] | null;
   translated: object;
   updatedAt: Date | string | null;
-  extenstions: object;
+  extensions?: object;
+  customFields: CustomFields;
+  apiAlias: string;
 };
diff --git a/packages/types/shopware-6-client/models/system/tax/CalculatedTax.d.ts b/packages/types/shopware-6-client/models/system/tax/CalculatedTax.d.ts
index 3b0a827d..6d8eb3cf 100644
--- a/packages/types/shopware-6-client/models/system/tax/CalculatedTax.d.ts
+++ b/packages/types/shopware-6-client/models/system/tax/CalculatedTax.d.ts
@@ -2,4 +2,5 @@ export type CalculatedTax = {
   tax: number;
   taxRate: number;
   price: number;
+  apiAlias: "cart_tax_calculated";
 };
diff --git a/packages/types/shopware-6-client/models/system/tax/Tax.d.ts b/packages/types/shopware-6-client/models/system/tax/Tax.d.ts
index 34b6bf74..b8b465ce 100644
--- a/packages/types/shopware-6-client/models/system/tax/Tax.d.ts
+++ b/packages/types/shopware-6-client/models/system/tax/Tax.d.ts
@@ -7,6 +7,12 @@ import { CustomFields } from "../../common/CustomField";
 export type Tax = {
   taxRate: number;
   name: string;
-  products: Product[] | null;
-  customFields: CustomFields;
+  products?: Product[] | null;
+  customFields: CustomFields | null;
+  translated: [];
+  createdAt: Date | string;
+  updatedAt: Date | string | null;
+  position: number;
+  id: string;
+  apiAlias: string;
 };
diff --git a/packages/types/shopware-6-client/models/system/tax/TaxRule.d.ts b/packages/types/shopware-6-client/models/system/tax/TaxRule.d.ts
index c4b4f4fc..101f7951 100644
--- a/packages/types/shopware-6-client/models/system/tax/TaxRule.d.ts
+++ b/packages/types/shopware-6-client/models/system/tax/TaxRule.d.ts
@@ -1,4 +1,5 @@
 export type TaxRule = {
   taxRate: number;
   percentage: number;
+  apiAlias: "cart_tax_rule";
 };
diff --git a/packages/types/shopware-6-client/models/system/unit/Unit.d.ts b/packages/types/shopware-6-client/models/system/unit/Unit.d.ts
index e8e666f6..aed18949 100644
--- a/packages/types/shopware-6-client/models/system/unit/Unit.d.ts
+++ b/packages/types/shopware-6-client/models/system/unit/Unit.d.ts
@@ -8,5 +8,9 @@ export type Unit = {
   shortCode: string | null;
   name: string | null;
   translated: UnitTranslation | null;
-  customFields: CustomFields;
+  customFields: CustomFields | null;
+  createdAt: Date | string;
+  updatedAt: Date | string | null;
+  id: string;
+  apiAlias: string;
 };
diff --git a/packages/types/shopware-6-client/models/system/unit/UnitTranslation.d.ts b/packages/types/shopware-6-client/models/system/unit/UnitTranslation.d.ts
index 41bd4146..e44c2c38 100644
--- a/packages/types/shopware-6-client/models/system/unit/UnitTranslation.d.ts
+++ b/packages/types/shopware-6-client/models/system/unit/UnitTranslation.d.ts
@@ -5,9 +5,9 @@ import { CustomFields } from "../../common/CustomField";
  * @public
  */
 export type UnitTranslation = {
-  unitId: string;
+  unitId?: string;
   shortCode: string | null;
   name: string | null;
-  unit: Unit | null;
+  unit?: Unit | null;
   customFields: CustomFields;
 };
diff --git a/packages/types/shopware-6-client/response/ContextUpdateResult.ts b/packages/types/shopware-6-client/response/ContextUpdateResult.ts
index c8bfdee4..2ac8c97b 100644
--- a/packages/types/shopware-6-client/response/ContextUpdateResult.ts
+++ b/packages/types/shopware-6-client/response/ContextUpdateResult.ts
@@ -4,5 +4,5 @@
 export type ContextUpdateResult = {
   apiAlias: "array_struct";
   contextToken: string;
-  redirectUrl: string;
+  redirectUrl: null | string;
 };
diff --git a/templates/vue-demo-store/app.vue b/templates/vue-demo-store/app.vue
index 0a07029a..7116a217 100644
--- a/templates/vue-demo-store/app.vue
+++ b/templates/vue-demo-store/app.vue
@@ -28,12 +28,18 @@ sessionContextData.value = await getSessionContext(apiInstance);
 //   }
 // );
 
-// read the locale from accept-language header (i.e. en-GB or de-DE)
+// read the locale/lang code from accept-language header (i.e. en, en-GB or de-DE)
 // and set configuration for price formatting globally
 const headers = useRequestHeaders();
+// Extract the first (with highest priority order) locale or lang code from accept-language header
+// for example: "en-US;q=0.7,en;q=0.3" will return "en-US"
 const localeFromHeader = headers?.["accept-language"]
   ?.split(",")
-  ?.find((languageConfig) => languageConfig.match(/([a-z]){2}\-([A-Z]{2})/));
+  ?.map(
+    (languageConfig) => languageConfig.match(/^([a-z]{2}(?:-[A-Z]{2})?)/)?.[0],
+  )
+  .find(Boolean);
+
 usePrice({
   currencyCode: sessionContextData.value?.currency?.isoCode || "",
   localeCode: localeFromHeader,


Test patch:
diff --git a/apps/e2e-tests/playwright.config.ts b/apps/e2e-tests/playwright.config.ts
index 6b304990..32c71ef1 100644
--- a/apps/e2e-tests/playwright.config.ts
+++ b/apps/e2e-tests/playwright.config.ts
@@ -62,12 +62,12 @@ const config: PlaywrightTestConfig = {
       },
     },
 
-    {
-      name: "Desktop Firefox",
-      use: {
-        ...devices["Desktop Firefox"],
-      },
-    },
+    // {
+    //   name: "Desktop Firefox",
+    //   use: {
+    //     ...devices["Desktop Firefox"],
+    //   },
+    // },
 
     /* Test against mobile viewports. */
     // {
diff --git a/packages/api-client-next/src/errorInterceptor.test.ts b/packages/api-client-next/src/errorInterceptor.test.ts
new file mode 100644
index 00000000..0e4fc92e
--- /dev/null
+++ b/packages/api-client-next/src/errorInterceptor.test.ts
@@ -0,0 +1,67 @@
+import { describe, expect, it } from "vitest";
+import { ApiError, ApiClientError, errorInterceptor } from "./errorInterceptor";
+import { FetchResponse } from "ofetch";
+
+describe("errorInterceptor", () => {
+  it("should extract different error messages from response to match snapshot", async () => {
+    const resp = {
+      _data: {
+        errors: [
+          {
+            title: "Constraint violation error",
+            detail:
+              'The "salutation" entity with id "1b341372add24e318d4aa33564245d67" does not exist.',
+            source: {
+              pointer: "/salutationId",
+            },
+          },
+          {
+            title: "Constraint violation error",
+            detail: "The value you selected is not a valid choice.",
+            source: {
+              pointer: "/storefrontUrl",
+            },
+          },
+          {
+            title: "Constraint violation error",
+            detail:
+              'The "salutation" entity with id "1b341372add24e318d4aa33564245d67" does not exist.',
+            source: {
+              pointer: "/billingAddress/salutationId",
+            },
+          },
+          {
+            code: "0",
+            status: "500",
+            title: "Internal Server Error",
+            detail: "Empty ids provided in criteria",
+          },
+        ],
+      },
+    } as unknown as FetchResponse<{
+      errors: Array<ApiError>;
+    }>;
+
+    await expect(() =>
+      errorInterceptor(resp),
+    ).toThrowErrorMatchingInlineSnapshot(
+      `
+      "Failed request
+       - [Constraint violation error][/salutationId] The \\"salutation\\" entity with id \\"1b341372add24e318d4aa33564245d67\\" does not exist.
+       - [Constraint violation error][/storefrontUrl] The value you selected is not a valid choice.
+       - [Constraint violation error][/billingAddress/salutationId] The \\"salutation\\" entity with id \\"1b341372add24e318d4aa33564245d67\\" does not exist.
+       - [Internal Server Error] Empty ids provided in criteria"
+    `,
+    );
+
+    try {
+      errorInterceptor(resp);
+    } catch (error) {
+      expect(error instanceof ApiClientError).toBe(true);
+
+      if (error instanceof ApiClientError) {
+        expect(error.details.errors.length).toBe(resp!._data!.errors.length);
+      }
+    }
+  });
+});
diff --git a/packages/api-client-next/src/invocations.e2e.test.ts b/packages/api-client-next/src/invocations.e2e.test.ts
index 42582141..e6d15616 100644
--- a/packages/api-client-next/src/invocations.e2e.test.ts
+++ b/packages/api-client-next/src/invocations.e2e.test.ts
@@ -18,7 +18,10 @@ describe("Test real API invocations", () => {
         name: "qwe",
       }),
     ).rejects.toThrowErrorMatchingInlineSnapshot(
-      '" (401 Unauthorized (https://demo-frontends.shopware.store/store-api/checkout/cart?name=qwe))"',
+      `
+      "Failed request
+       - [Unauthorized] Header \\"sw-access-key\\" is required."
+    `,
     );
   });