@scayle/storefront-product-detail 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,74 @@
1
+ # @scayle/storefront-product-detail
2
+
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - **\[SEO\]** Update product JSON-LD data to use the `ProductGroup` schema.
8
+
9
+ ## 1.0.2
10
+
11
+ ### Patch Changes
12
+
13
+ - Ensure the module meta has the correct package version
14
+
15
+ ## 1.0.1
16
+
17
+ ### Patch Changes
18
+
19
+ - We've updated to `nuxt@3.14`
20
+
21
+ ## 1.0.0
22
+
23
+ ### Major Changes
24
+
25
+ - This release introduces the `@scayle/storefront-product-detail` package, decoupling composables and utilities of the Product Detail Page (PDP) functionality from the SCAYLE Storefront Boilerplate for enhanced modularity and integration.
26
+ This separation empowers developers with greater control over PDP updates and simplifies its integration into existing Storefront-based projects.
27
+
28
+ - [Discover more about the Product Detail Page for SCAYLE Storefront in our SCAYLE Resource Center.](https://scayle.dev/en/storefront-guide/developer-guide/features/pages/product-detail-page)
29
+
30
+ - This release requires `@scayle/storefront-nuxt@8.x` or higher. Support for `@scayle/storefront-nuxt@7.x` has been discontinued. Please update your dependencies accordingly. The `peerDependency` range has been updated to `^8.0.0`.
31
+
32
+ ### Minor Changes
33
+
34
+ - Compared to the original implementation within the SCAYLE Storefront Boilerplate, the `@scayle/storefront-product-detail` package has received some improvements and refactoring:
35
+
36
+ - Introduced the `getCombineWithProductIds` function within the product utility, enabling efficient retrieval of product combinations within product listings.
37
+ - Added the `getFilteredAttributeGroups` function to the attribute utility, simplifying the retrieval and formatting of filtered product attributes for display in listings.
38
+ - Introduced the `useProductSeoData` composable, automating the generation of SEO-critical metadata for product pages. This includes dynamically generated canonical links, robots meta tags, product titles, and structured data (`JSON-LD`) for product details and breadcrumbs, enhancing search engine visibility.
39
+
40
+ ## 0.4.0
41
+
42
+ ### Minor Changes
43
+
44
+ - Fix computed `sortLinks` return type mismatch in `UseProductListSortReturn` interface.
45
+
46
+ ## 0.3.0
47
+
48
+ ### Minor Changes
49
+
50
+ - Fix `sortLinks` type by extending the `SelectedSort` type with `to` property which represents the `RouteLocationRaw` type from `vue-router`.
51
+
52
+ ## 0.2.0
53
+
54
+ ### Minor Changes
55
+
56
+ - Add and expose explicit return types/interfaces externally for all product detail composables.
57
+
58
+ ## 0.1.2
59
+
60
+ ### Patch Changes
61
+
62
+ - Update import of test factories to use new entry point
63
+
64
+ ## 0.1.1
65
+
66
+ ### Patch Changes
67
+
68
+ - Resolved issue where sorting did not correctly route to the first page.
69
+
70
+ ## 0.1.0
71
+
72
+ ### Minor Changes
73
+
74
+ - Introduce `@scayle/storefront-product-listing` package
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 SCAYLE GmbH
3
+ Copyright (c) 2025 SCAYLE GmbH
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/module.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@scayle/storefront-product-detail",
3
3
  "configKey": "product-detail",
4
- "version": "1.0.0",
4
+ "version": "1.1.0",
5
5
  "compatibility": {
6
6
  "bridge": false,
7
7
  "nuxt": ">=3.13"
8
8
  },
9
9
  "builder": {
10
10
  "@nuxt/module-builder": "0.8.4",
11
- "unbuild": "3.0.1"
11
+ "unbuild": "3.3.1"
12
12
  }
13
13
  }
package/dist/module.mjs CHANGED
@@ -1,10 +1,12 @@
1
1
  import { defineNuxtModule, createResolver, addImportsDir } from '@nuxt/kit';
2
2
 
3
+ const PACKAGE_NAME = "@scayle/storefront-product-detail";
4
+ const PACKAGE_VERSION = "1.1.0";
3
5
  const module = defineNuxtModule({
4
6
  meta: {
5
- name: "@scayle/storefront-product-detail",
7
+ name: PACKAGE_NAME,
6
8
  configKey: "product-detail",
7
- version: "1.0.0",
9
+ version: PACKAGE_VERSION,
8
10
  compatibility: {
9
11
  bridge: false,
10
12
  nuxt: ">=3.13"
@@ -1,19 +1,24 @@
1
- import { type ComputedRef, type MaybeRefOrGetter } from 'vue';
2
- import type { BreadcrumbItem, Variant } from '@scayle/storefront-nuxt';
3
- import type { WithContext, Product, BreadcrumbList } from 'schema-dts';
1
+ import type { ComputedRef, MaybeRefOrGetter } from 'vue';
2
+ import type { BreadcrumbItem } from '@scayle/storefront-nuxt';
3
+ import type { WithContext, BreadcrumbList, ProductGroup, Product } from 'schema-dts';
4
4
  type CanonicalLink = Record<'rel' | 'key' | 'href', string>;
5
5
  type UrlParams = Record<'baseUrl' | 'fullPath', string>;
6
6
  interface ProductInfo {
7
+ /** The id of the product. */
8
+ productId: number;
7
9
  /** The name of the product. */
8
10
  name: string;
9
11
  /** The brand of the product. */
10
12
  brand: string;
11
13
  /** A description of the product. */
12
14
  productDescription: string;
15
+ /** The color of the product. */
16
+ color: string;
13
17
  /** Array of product variants. */
14
- variants: Variant[];
18
+ variants: WithContext<Product>[];
15
19
  /** Array of image URLs for the product. */
16
20
  images: string[];
21
+ variesBy?: ProductGroup['variesBy'];
17
22
  }
18
23
  export interface UseProductSeoDataReturn {
19
24
  /** The title of the product. */
@@ -22,8 +27,8 @@ export interface UseProductSeoDataReturn {
22
27
  robots: string;
23
28
  /** Computed ref for canonical link tag metadata. */
24
29
  canonicalLink: ComputedRef<CanonicalLink[]>;
25
- /** Computed ref containing JSON-LD structured data for the product. */
26
- productJsonLd: ComputedRef<WithContext<Product>>;
30
+ /** Computed ref containing JSON-LD structured data for the product group. */
31
+ productJsonLd: ComputedRef<WithContext<ProductGroup>>;
27
32
  /** Computed ref containing JSON-LD structured data for product breadcrumbs. */
28
33
  productBreadcrumbJsonLd: ComputedRef<WithContext<BreadcrumbList>>;
29
34
  }
@@ -3,7 +3,7 @@ import {
3
3
  generateCategoryBreadcrumbSchema,
4
4
  sanitizeCanonicalURL
5
5
  } from "@scayle/storefront-nuxt";
6
- import { generateProductSchema } from "../utils/seo.js";
6
+ import { generateProductGroupSchema } from "../utils/seo.js";
7
7
  export function useProductSeoData(breadcrumbs, urlParams, productInfo) {
8
8
  const canonicalUrl = computed(() => {
9
9
  const url = `${toValue(urlParams).baseUrl}${toValue(urlParams).fullPath}`;
@@ -14,13 +14,16 @@ export function useProductSeoData(breadcrumbs, urlParams, productInfo) {
14
14
  });
15
15
  const robots = "index, follow";
16
16
  const productJsonLd = computed(() => {
17
- return generateProductSchema({
18
- productName: toValue(productInfo).name,
19
- brandName: toValue(productInfo).brand,
20
- url: canonicalUrl.value,
21
- variants: toValue(productInfo).variants,
22
- images: toValue(productInfo).images,
23
- description: toValue(productInfo).productDescription
17
+ const info = toValue(productInfo);
18
+ return generateProductGroupSchema({
19
+ productId: info.productId.toString(),
20
+ name: info.name,
21
+ description: info.productDescription,
22
+ images: info.images,
23
+ brandName: info.brand,
24
+ color: info.color,
25
+ variants: info.variants,
26
+ variesBy: info.variesBy
24
27
  });
25
28
  });
26
29
  const productBreadcrumbJsonLd = computed(() => {
@@ -1,4 +1,4 @@
1
- import { type AdvancedAttribute } from '@scayle/storefront-nuxt';
1
+ import type { AdvancedAttribute } from '@scayle/storefront-nuxt';
2
2
  /**
3
3
  * Extracts and returns an array of product IDs from an advanced attribute.
4
4
  *
@@ -1,6 +1,4 @@
1
- import {
2
- getFlattenedAdvancedAttribute
3
- } from "@scayle/storefront-nuxt";
1
+ import { getFlattenedAdvancedAttribute } from "@scayle/storefront-nuxt";
4
2
  export const getCombineWithProductIds = (attribute) => {
5
3
  if (!attribute) {
6
4
  return [];
@@ -1,5 +1,5 @@
1
1
  import type { Variant } from '@scayle/storefront-nuxt';
2
- import type { Product, WithContext } from 'schema-dts';
2
+ import type { Product, WithContext, ProductGroup } from 'schema-dts';
3
3
  /**
4
4
  * Generates an object following the schema.org `Product` type with context.
5
5
  *
@@ -8,11 +8,19 @@ import type { Product, WithContext } from 'schema-dts';
8
8
  *
9
9
  * @returns An object following the schema.org `Product` type with context.
10
10
  */
11
- export declare const generateProductSchema: ({ productName, brandName, variants, url, images, description, }: {
11
+ export declare const generateProductSchema: ({ productName, variant, url, size, }: {
12
12
  productName: string;
13
- brandName: string;
14
- variants: Variant[];
13
+ variant: Variant;
15
14
  url: string;
16
- description: string;
17
- images?: string[];
15
+ size?: string;
18
16
  }) => WithContext<Product>;
17
+ export declare const generateProductGroupSchema: ({ productId, name, description, images, brandName, color, variants, variesBy, }: {
18
+ productId: string;
19
+ name: string;
20
+ description: string;
21
+ images: string[];
22
+ brandName: string;
23
+ color: string;
24
+ variants: Product[];
25
+ variesBy?: ProductGroup["variesBy"];
26
+ }) => WithContext<ProductGroup>;
@@ -1,38 +1,58 @@
1
1
  import { isInStock } from "@scayle/storefront-nuxt";
2
- const generateSchemaProductOffers = ({
3
- variants
2
+ const generateSchemaProductOffer = ({
3
+ variant,
4
+ baseUrl
4
5
  }) => {
5
- return variants.map((variant) => ({
6
+ const url = new URL(baseUrl);
7
+ url.searchParams.append("variantId", variant.id.toString());
8
+ return {
6
9
  "@type": "Offer",
10
+ url: url.href,
7
11
  mpn: variant.referenceKey,
8
12
  sku: variant.id.toString(),
9
13
  price: variant.price.withTax / 100,
10
14
  priceCurrency: variant.price.currencyCode,
11
15
  availability: isInStock(variant) ? "https://schema.org/InStock" : "https://schema.org/OutOfStock",
12
16
  itemCondition: "https://schema.org/NewCondition"
13
- }));
17
+ };
14
18
  };
15
19
  export const generateProductSchema = ({
16
20
  productName,
17
- brandName,
18
- variants,
21
+ variant,
19
22
  url,
20
- images = [],
21
- description
23
+ size
22
24
  }) => {
23
- const offers = generateSchemaProductOffers({ variants });
24
- const brand = {
25
- "@type": "Brand",
26
- name: brandName
27
- };
28
25
  return {
29
26
  "@context": "https://schema.org",
30
27
  "@type": "Product",
31
- url,
32
- name: productName,
33
- image: images,
28
+ name: `${productName} (size)`,
29
+ size,
30
+ offers: generateSchemaProductOffer({ variant, baseUrl: url })
31
+ };
32
+ };
33
+ export const generateProductGroupSchema = ({
34
+ productId,
35
+ name,
36
+ description,
37
+ images,
38
+ brandName,
39
+ color,
40
+ variants,
41
+ variesBy
42
+ }) => {
43
+ return {
44
+ "@context": "https://schema.org",
45
+ "@type": "ProductGroup",
46
+ productID: productId,
47
+ name: `${name}, ${color}`,
34
48
  description,
35
- brand,
36
- offers
49
+ image: images,
50
+ brand: {
51
+ "@type": "Brand",
52
+ name: brandName
53
+ },
54
+ color,
55
+ ...variants.length ? { hasVariant: variants } : {},
56
+ ...variesBy ? { variesBy } : {}
37
57
  };
38
58
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scayle/storefront-product-detail",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Collection of essential composables and utilities to work with product detail",
5
5
  "author": "SCAYLE Commerce Engine",
6
6
  "license": "MIT",
@@ -19,6 +19,7 @@
19
19
  "main": "./dist/module.cjs",
20
20
  "types": "./dist/types.d.ts",
21
21
  "files": [
22
+ "CHANGELOG.md",
22
23
  "dist"
23
24
  ],
24
25
  "imports": {
@@ -35,7 +36,7 @@
35
36
  "format:fix": "dprint fmt",
36
37
  "test": "vitest --run --passWithNoTests",
37
38
  "test:watch": "vitest --passWithNoTests",
38
- "test:ci": "vitest --run --passWithNoTests --coverage --reporter=default --reporter=junit --outputFile=./junit.xml",
39
+ "test:ci": "vitest --run --passWithNoTests --coverage --reporter=default --reporter=junit --outputFile=./coverage/junit.xml",
39
40
  "typecheck": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit",
40
41
  "package:lint": "publint"
41
42
  },
@@ -43,28 +44,28 @@
43
44
  "@nuxt/kit": "^3.13.0",
44
45
  "@scayle/storefront-nuxt": "^8.0.0",
45
46
  "@vue/test-utils": "2.4.6",
46
- "@vueuse/core": "12.0.0",
47
+ "@vueuse/core": "12.7.0",
47
48
  "vue-router": "4.5.0"
48
49
  },
49
50
  "devDependencies": {
50
51
  "@nuxt/kit": "3.14.1592",
51
52
  "@nuxt/module-builder": "0.8.4",
52
53
  "@nuxt/schema": "3.14.1592",
53
- "@nuxt/test-utils": "3.15.1",
54
- "@scayle/eslint-config-storefront": "4.4.0",
55
- "@scayle/storefront-nuxt": "8.1.4",
56
- "@types/node": "22.10.2",
54
+ "@nuxt/test-utils": "3.15.4",
55
+ "@scayle/eslint-config-storefront": "4.4.1",
56
+ "@scayle/storefront-nuxt": "8.10.3",
57
+ "@types/node": "22.13.4",
57
58
  "@vue/test-utils": "2.4.6",
58
- "@vueuse/core": "12.0.0",
59
- "dprint": "0.47.6",
60
- "eslint": "9.17.0",
61
- "happy-dom": "15.11.7",
59
+ "@vueuse/core": "12.7.0",
60
+ "dprint": "0.49.0",
61
+ "eslint": "9.20.1",
62
+ "happy-dom": "17.1.1",
62
63
  "eslint-formatter-gitlab": "5.1.0",
63
64
  "nuxt": "3.14.1592",
64
65
  "publint": "0.2.12",
65
- "typescript": "5.6.3",
66
- "vitest": "2.1.8",
66
+ "typescript": "5.7.3",
67
+ "vitest": "2.1.9",
67
68
  "vue-router": "4.5.0",
68
- "vue-tsc": "2.1.10"
69
+ "vue-tsc": "2.2.2"
69
70
  }
70
71
  }
@@ -1 +0,0 @@
1
- export {};
@@ -1,179 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { useProductSeoData } from "~/src/runtime";
3
- import { variantFactory } from "@scayle/storefront-nuxt/dist/test/factories";
4
- import { ref } from "vue";
5
- describe("useProductSeoData", () => {
6
- it("should return product seo data from product", () => {
7
- const {
8
- title,
9
- productBreadcrumbJsonLd,
10
- productJsonLd,
11
- robots,
12
- canonicalLink
13
- } = useProductSeoData(
14
- [{ value: "value", to: "/" }],
15
- { baseUrl: "baseUrl", fullPath: "fullPath" },
16
- {
17
- name: "name",
18
- variants: [variantFactory.build({ id: 1 })],
19
- brand: "brand",
20
- productDescription: "description",
21
- images: []
22
- }
23
- );
24
- expect(title.value).toBe("name");
25
- expect(productBreadcrumbJsonLd.value).toStrictEqual({
26
- "@context": "https://schema.org",
27
- "@type": "BreadcrumbList",
28
- "itemListElement": [
29
- {
30
- "@type": "ListItem",
31
- "item": "/",
32
- "name": "value",
33
- "position": 1
34
- }
35
- ]
36
- });
37
- expect(productJsonLd.value).toStrictEqual({
38
- "@context": "https://schema.org",
39
- "@type": "Product",
40
- "brand": {
41
- "@type": "Brand",
42
- "name": "brand"
43
- },
44
- "description": "description",
45
- "image": [],
46
- "name": "name",
47
- "offers": [
48
- {
49
- "@type": "Offer",
50
- "availability": "https://schema.org/InStock",
51
- "itemCondition": "https://schema.org/NewCondition",
52
- "mpn": void 0,
53
- "price": 1,
54
- "priceCurrency": "USD",
55
- "sku": "1"
56
- }
57
- ],
58
- "url": "baseUrlfullPath"
59
- });
60
- expect(robots).toBe("index, follow");
61
- expect(canonicalLink.value).toStrictEqual([
62
- {
63
- "href": "baseUrlfullPath",
64
- "key": "canonical",
65
- "rel": "canonical"
66
- }
67
- ]);
68
- });
69
- it("should handle reactivity correctly with changing parameters", () => {
70
- const breadcrubs = [{ value: "value", to: "/" }];
71
- const urlParams = { baseUrl: "baseUrl", fullPath: "fullPath" };
72
- const productInfo = ref({
73
- name: "name",
74
- variants: [variantFactory.build({ id: 1 })],
75
- brand: "brand",
76
- productDescription: "description",
77
- images: []
78
- });
79
- const {
80
- title,
81
- productBreadcrumbJsonLd,
82
- productJsonLd,
83
- canonicalLink
84
- } = useProductSeoData(
85
- breadcrubs,
86
- urlParams,
87
- productInfo
88
- );
89
- expect(title.value).toBe("name");
90
- expect(productBreadcrumbJsonLd.value).toStrictEqual({
91
- "@context": "https://schema.org",
92
- "@type": "BreadcrumbList",
93
- "itemListElement": [
94
- {
95
- "@type": "ListItem",
96
- "item": "/",
97
- "name": "value",
98
- "position": 1
99
- }
100
- ]
101
- });
102
- expect(productJsonLd.value).toStrictEqual({
103
- "@context": "https://schema.org",
104
- "@type": "Product",
105
- "brand": {
106
- "@type": "Brand",
107
- "name": "brand"
108
- },
109
- "description": "description",
110
- "image": [],
111
- "name": "name",
112
- "offers": [
113
- {
114
- "@type": "Offer",
115
- "availability": "https://schema.org/InStock",
116
- "itemCondition": "https://schema.org/NewCondition",
117
- "mpn": void 0,
118
- "price": 1,
119
- "priceCurrency": "USD",
120
- "sku": "1"
121
- }
122
- ],
123
- "url": "baseUrlfullPath"
124
- });
125
- expect(canonicalLink.value).toStrictEqual([
126
- {
127
- "href": "baseUrlfullPath",
128
- "key": "canonical",
129
- "rel": "canonical"
130
- }
131
- ]);
132
- breadcrubs[0].to = "/other";
133
- urlParams.baseUrl = "otherUrl";
134
- productInfo.value.name = "otherName";
135
- expect(title.value).toBe("otherName");
136
- expect(productBreadcrumbJsonLd.value).toStrictEqual({
137
- "@context": "https://schema.org",
138
- "@type": "BreadcrumbList",
139
- "itemListElement": [
140
- {
141
- "@type": "ListItem",
142
- "item": "/other",
143
- "name": "value",
144
- "position": 1
145
- }
146
- ]
147
- });
148
- expect(productJsonLd.value).toStrictEqual({
149
- "@context": "https://schema.org",
150
- "@type": "Product",
151
- "brand": {
152
- "@type": "Brand",
153
- "name": "brand"
154
- },
155
- "description": "description",
156
- "image": [],
157
- "name": "otherName",
158
- "offers": [
159
- {
160
- "@type": "Offer",
161
- "availability": "https://schema.org/InStock",
162
- "itemCondition": "https://schema.org/NewCondition",
163
- "mpn": void 0,
164
- "price": 1,
165
- "priceCurrency": "USD",
166
- "sku": "1"
167
- }
168
- ],
169
- "url": "otherUrlfullPath"
170
- });
171
- expect(canonicalLink.value).toStrictEqual([
172
- {
173
- "href": "otherUrlfullPath",
174
- "key": "canonical",
175
- "rel": "canonical"
176
- }
177
- ]);
178
- });
179
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,39 +0,0 @@
1
- import { it, describe, expect } from "vitest";
2
- import { attributeGroupFactory } from "@scayle/storefront-nuxt/dist/test/factories";
3
- import { getFilteredAttributeGroups } from "./attribute.js";
4
- describe("attribute", () => {
5
- describe("getFilteredAttributeGroups", () => {
6
- it("should return correct filtered and formatted attributes", () => {
7
- const attributes = {
8
- design: attributeGroupFactory.build({
9
- type: "design",
10
- label: "Jackenart",
11
- values: { id: 2534, label: "Bomberjacke", value: "bomberjacke" }
12
- }),
13
- extras: attributeGroupFactory.build({
14
- label: "Extras",
15
- type: "extras",
16
- multiSelect: true,
17
- values: [
18
- { id: 2427, label: "Weicher Griff", value: "weicher_griff" },
19
- { id: 2435, label: "Ton-in-Ton-N\xE4hte", value: "tonintonnhte" },
20
- { id: 2438, label: "Label-Stickerei", value: "labelstickerei" }
21
- ]
22
- })
23
- };
24
- const formattedAttributes = getFilteredAttributeGroups(
25
- attributes,
26
- ["design", "extras"]
27
- );
28
- expect(formattedAttributes).toHaveLength(2);
29
- expect(formattedAttributes.get("design")).toStrictEqual([
30
- "Jackenart: Bomberjacke"
31
- ]);
32
- expect(formattedAttributes.get("extras")).toStrictEqual([
33
- "Weicher Griff",
34
- "Ton-in-Ton-N\xE4hte",
35
- "Label-Stickerei"
36
- ]);
37
- });
38
- });
39
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,97 +0,0 @@
1
- import { it, describe } from "vitest";
2
- import { getCombineWithProductIds } from "~/src/runtime/utils/product";
3
- import { advancedAttributeFactory } from "@scayle/storefront-nuxt/dist/test/factories";
4
- describe("getCombineWithProductIds", () => {
5
- it("should return productIds from data", ({ expect }) => {
6
- const combineWithAttribute = advancedAttributeFactory.build({
7
- key: "combineWith",
8
- label: "Kombiniere mit",
9
- values: [
10
- {
11
- fieldSet: [
12
- [
13
- {
14
- value: "206131"
15
- }
16
- ]
17
- ],
18
- groupSet: []
19
- },
20
- {
21
- fieldSet: [
22
- [
23
- {
24
- value: "206017"
25
- }
26
- ]
27
- ],
28
- groupSet: []
29
- },
30
- {
31
- fieldSet: [
32
- [
33
- {
34
- value: "206012"
35
- }
36
- ]
37
- ],
38
- groupSet: []
39
- }
40
- ]
41
- });
42
- expect(getCombineWithProductIds(combineWithAttribute)).toStrictEqual([
43
- 206131,
44
- 206017,
45
- 206012
46
- ]);
47
- });
48
- it("should return empty array when attribute is not available", ({ expect }) => {
49
- expect(getCombineWithProductIds(void 0)).toStrictEqual([]);
50
- });
51
- it("should return productIds ignore invalid data", ({ expect }) => {
52
- const combineWithAttributeWithInvalidData = advancedAttributeFactory.build({
53
- key: "combineWith",
54
- label: "Kombiniere mit",
55
- values: [
56
- {
57
- fieldSet: [
58
- [
59
- {
60
- value: "206131"
61
- }
62
- ],
63
- [
64
- {
65
- value: "TEST"
66
- }
67
- ]
68
- ],
69
- groupSet: []
70
- },
71
- {
72
- fieldSet: [
73
- [
74
- {
75
- value: "206017"
76
- }
77
- ]
78
- ],
79
- groupSet: []
80
- },
81
- {
82
- fieldSet: [
83
- [
84
- {
85
- value: "206012"
86
- }
87
- ]
88
- ],
89
- groupSet: []
90
- }
91
- ]
92
- });
93
- expect(
94
- getCombineWithProductIds(combineWithAttributeWithInvalidData)
95
- ).toStrictEqual([206131, 206017, 206012]);
96
- });
97
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,38 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { generateProductSchema } from "~/src/runtime/utils/seo";
3
- import { variantFactory } from "@scayle/storefront-nuxt/dist/test/factories";
4
- describe("generateProductSchema", () => {
5
- it("should return productSchema from product", () => {
6
- const productSchema = generateProductSchema({
7
- productName: "productName",
8
- brandName: "brandName",
9
- variants: [variantFactory.build({ id: 1 })],
10
- url: "url",
11
- images: [],
12
- description: "description"
13
- });
14
- expect(productSchema).toStrictEqual({
15
- "@context": "https://schema.org",
16
- "@type": "Product",
17
- "brand": {
18
- "@type": "Brand",
19
- "name": "brandName"
20
- },
21
- "description": "description",
22
- "image": [],
23
- "name": "productName",
24
- "offers": [
25
- {
26
- "@type": "Offer",
27
- "availability": "https://schema.org/InStock",
28
- "itemCondition": "https://schema.org/NewCondition",
29
- "mpn": void 0,
30
- "price": 1,
31
- "priceCurrency": "USD",
32
- "sku": "1"
33
- }
34
- ],
35
- "url": "url"
36
- });
37
- });
38
- });