Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: generate breadcrumbs #468

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/web/composables/useProduct/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ export interface UseProductState {
}

export type FetchProduct = (params: ProductParams) => Promise<Product>;
export type GenerateBreadcrumb = () => void;
export type SetBreadcrumbs = () => void;

export interface UseProduct {
data: Readonly<Ref<UseProductState['data']>>;
loading: Readonly<Ref<boolean>>;
breadcrumbs: Readonly<Ref<UseProductState['breadcrumbs']>>;
fetchProduct: FetchProduct;
generateBreadcrumbs: GenerateBreadcrumb;
setBreadcrumbs: SetBreadcrumbs;
setTitle: () => void;
properties: UseProductOrderProperties;
}
Expand Down
21 changes: 9 additions & 12 deletions apps/web/composables/useProduct/useProduct.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { Product, ProductParams } from '@plentymarkets/shop-api';
import { categoryTreeGetters, productGetters } from '@plentymarkets/shop-sdk';
import { productGetters } from '@plentymarkets/shop-sdk';
import { toRefs } from '@vueuse/shared';
import type { UseProductReturn, UseProductState, FetchProduct } from '~/composables/useProduct/types';
import { useSdk } from '~/sdk';
import { generateBreadcrumbs } from '~/utils/productHelper';

/**
* @description Composable managing product data
Expand Down Expand Up @@ -43,19 +45,14 @@ export const useProduct: UseProductReturn = (slug) => {
};

/**
* @description Function for generating breadcrumbs
* @example generateBreadcrumbs()
* @description Function for setting breadcrumbs
* @example setBreadcrumbs()
*/
const generateBreadcrumbs = () => {
const setBreadcrumbs = () => {
const { data: categoryTree } = useCategoryTree();
const { t } = useI18n();
const breadcrumb = categoryTreeGetters.generateBreadcrumbFromCategory(
categoryTree.value,
Number(productGetters.getCategoryIds(state.value.data)?.[0] ?? 0),
);
breadcrumb.unshift({ name: t('home'), link: '/' });
breadcrumb.push({ name: productGetters.getName(state.value.data), link: `#` });
state.value.breadcrumbs = breadcrumb;

state.value.breadcrumbs = generateBreadcrumbs(categoryTree.value, state.value.data, t('home'));
};

/**
Expand All @@ -71,7 +68,7 @@ export const useProduct: UseProductReturn = (slug) => {

return {
setTitle,
generateBreadcrumbs,
setBreadcrumbs,
fetchProduct,
...toRefs(state.value),
properties,
Expand Down
4 changes: 2 additions & 2 deletions apps/web/pages/product/[slug].vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ definePageMeta({
});

const { productParams, productId } = createProductParams(route.params);
const { data: product, fetchProduct, setTitle, generateBreadcrumbs, breadcrumbs } = useProduct(productId);
const { data: product, fetchProduct, setTitle, setBreadcrumbs, breadcrumbs } = useProduct(productId);
const { data: productReviewAverage, fetchProductReviewAverage } = useProductReviewAverage(productId);
const { fetchProductReviews } = useProductReviews(Number(productId));
if (process.server) {
Expand All @@ -64,7 +64,7 @@ if (process.server) {
}
selectVariation(productParams.variationId ? product.value : ({} as Product));
setTitle();
generateBreadcrumbs();
setBreadcrumbs();
// eslint-disable-next-line unicorn/expiring-todo-comments
/* TODO: This should only be temporary.
* It changes the url of the product page while on the page and switching the locale.
Expand Down
31 changes: 31 additions & 0 deletions apps/web/utils/__tests__/__fixtures__/CategoryTree.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { CategoryTreeItem } from "@plentymarkets/shop-api";

export const CategoryTreeFixture: CategoryTreeItem[] = [
{
"id": 73,
"type": "item",
"childCount": 0,
"itemCount": [
{
"categoryId": "73",
"webstoreId": "0",
"lang": "en",
"count": "1",
"createdAt": "2024-01-01T00:00:00+02:00",
"updatedAt": "2024-01-01T00:00:00+02:00",
"variationCount": "1",
"customerClassId": "0"
}
],
"details": [
{
"lang": "en",
"name": "Gear",
"nameUrl": "gear",
"metaTitle": "",
"imagePath": null,
"image2Path": null
}
]
}
]
168 changes: 168 additions & 0 deletions apps/web/utils/__tests__/__fixtures__/Product.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import type { Product } from "@plentymarkets/shop-api";

export const ProductFixture: Product = {
defaultCategories: [
{
id: 73,
parentCategoryId: null,
level: 1,
type: 'item',
linklist: true,
right: 'all',
sitemap: true,
updatedAt: '2024-01-01T00:00:00+02:00',
manually: true,
plentyId: 60796
}
],
filter: {
hasManufacturer: false,
isSalable: true,
isSalableAndActive: true,
hasActiveChildren: false
},
images: {
all: [
{
path: 'S3:157:gear-headphones-01.png',
names: [],
position: 0,
url: 'https://cdn02.plentymarkets.com/mevofvd5omld/item/images/157/full/gear-headphones-01.png',
urlSecondPreview: 'https://cdn02.plentymarkets.com/mevofvd5omld/item/images/157/secondPreview/gear-headphones-01.png',
urlPreview: 'https://cdn02.plentymarkets.com/mevofvd5omld/item/images/157/preview/gear-headphones-01.png',
urlMiddle: 'https://cdn02.plentymarkets.com/mevofvd5omld/item/images/157/middle/gear-headphones-01.png',
cleanImageName: 'gear-headphones-01.png',
height: 1430,
width: 1430
}
],
variation: []
},
item: {
id: 157,
add_cms_page: '0',
condition: {
id: 0,
names: {
lang: 'en',
name: 'New'
}
},
storeSpecial: null,
manufacturerId: 0,
producingCountryId: 1,
revenueAccount: 0,
conditionApi: {
id: 0,
names: {
lang: 'en',
name: 'New'
}
},
ageRestriction: 0,
itemType: 'default',
producingCountry: {
storehouseId: 0,
names: {
name: 'Germany',
lang: 'en'
},
lang: 'de',
shippingDestinationId: 101,
id: 1,
isoCode3: 'DEU',
isCountryStateMandatory: null,
isoCode2: 'DE',
name: 'Germany',
active: 1
},
manufacturer: [],
rebate: 0,
salableVariationCount: 1,
customsTariffNumber: ''
},
variation: {
position: 1,
number: '1145',
model: '',
externalId: '',
availabilityId: 1,
maximumOrderQuantity: null,
minimumOrderQuantity: 1,
intervalOrderQuantity: 1,
availableUntil: null,
releasedAt: null,
name: '',
weightG: 0,
weightNetG: 0,
widthMM: 0,
lengthMM: 0,
heightMM: 0,
unitsContained: 51,
vatId: 0,
bundleType: null,
mayShowUnitPrice: true,
customsTariffNumber: '',
availability: {
id: 1,
icon: 'av1.gif',
averageDays: 2,
createdAt: '2022-06-27 09:20:16',
updatedAt: '2022-06-27 09:20:16',
names: {
id: 42,
availabilityId: 1,
lang: 'en',
name: 'Ready for shipping, delivery in 48h',
createdAt: '2022-06-27 09:20:16',
updatedAt: '2022-06-27 09:20:16'
},
iconPath: '/tpl/availability/av1.gif'
},
id: 1100,
unitCombinationId: 9,
availabilityUpdatedAt: '2022-02-15T16:56:01+01:00'
},
texts: {
name2: '',
id: 258,
description: '<h2 class="mb-4">Our most elegant piece</h2> <p>Crafted from a blend of organic pineapple peels and eucalyptus fibers this item resembles the most sustainable and yet elegant component in our collection. It comes in three different colors that were inspired by the Mongolian landscapes. Charcoal, Sand and Berry Red.&nbsp;</p> <div class="d-flex align-items-center justify-content-between py-4 px-0 px-md-4"><div class="d-inline-block text-center"><span class="fa-3x">&nbsp;</span> <div class="fa fa-3x fa-tree mb-2">&nbsp;</div> <p>Sustainable</p></div> <div class="d-inline-block text-center"><span class="fa-3x">&nbsp;</span> <div class="fa fa-3x fa-heart mb-2">&nbsp;</div> <p>Hand-crafted</p></div> <div class="d-inline-block text-center"><span class="fa-3x">&nbsp;</span> <div class="fa fa-3x fa-flask mb-2">&nbsp;</div> <p>Material science</p></div></div> <p class="mb-4"><img alt="Hand-crafted with love" src="https://cdn15.plentymarkets.com/ksvjcz2xpb12/frontend/story-handcrafted-1920x.jpg" ></p> <p>Every thread and every fibre is carefully manufactured in our local facilities. In addition, each piece goes through our strict quality assurance processes, so that you can enjoy flawless products, every time.</p>',
itemId: 157,
name3: '',
lang: 'en',
metaDescription: '',
urlPath: 'headphones-capybara',
technicalData: '<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. &nbsp;&nbsp;</p> <p>Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. &nbsp;&nbsp;</p> <p>Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. &nbsp;&nbsp;</p> <p>Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. &nbsp;&nbsp;</p> <p>Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. &nbsp;&nbsp;</p> <p>At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur</p>',
name1: 'Headphones Capybara',
shortDescription: '',
keywords: ''
},
unit: {
names: {
unitId: 5,
lang: 'en',
name: 'liter'
},
unitOfMeasurement: 'LTR',
content: 2
},
properties: [],
hasOrderProperties: false,
hasRequiredOrderProperty: false,
facets: [],
variationAttributeMap: {
variations: [
{
variationId: 1100,
isSalable: true,
unitCombinationId: 9,
unitId: expect.any(Number),
unitName: '2 liter',
attributes: []
}
],
attributes: []
}
};

export default ProductFixture;
15 changes: 15 additions & 0 deletions apps/web/utils/__tests__/productHelper.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { generateBreadcrumbs } from "../productHelper";
import { CategoryTreeFixture } from "./__fixtures__/CategoryTree";
import ProductFixture from "./__fixtures__/Product";

describe('product helper', () => {
it('should generate breadcrumbs', () => {
const breadcrumbs = generateBreadcrumbs(CategoryTreeFixture, ProductFixture, 'home');

expect(breadcrumbs).toStrictEqual([
{ name: 'home', link: '/' },
{ name: 'Gear', link: '/gear' },
{ name: "Headphones Capybara", link: "#" }
]);
})
})
14 changes: 13 additions & 1 deletion apps/web/utils/productHelper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { RouteParams } from 'vue-router';
import type { ProductParams } from '@plentymarkets/shop-api';
import type { Breadcrumb, CategoryTreeItem, Product, ProductParams } from '@plentymarkets/shop-api';
import { productGetters, categoryTreeGetters } from '@plentymarkets/shop-sdk';

export const createProductParams = (params: RouteParams) => {
const productPieces = (params.itemId as string).split('_');
Expand Down Expand Up @@ -41,3 +42,14 @@ export const updateProductURLPathForVariation = (

return pathSegments.join('/');
};

export const generateBreadcrumbs = (categoryTree: CategoryTreeItem[], product: Product, home: string): Breadcrumb[] => {
const categoryId = productGetters.getCategoryIds(product)?.[0] ?? 0;
const breadcrumbs = categoryTreeGetters.generateBreadcrumbFromCategory(categoryTree, Number(categoryId));
const productName = productGetters.getName(product);

breadcrumbs.unshift({ name: home, link: '/' });
breadcrumbs.push({ name: productName, link: `#` });

return breadcrumbs;
};
Loading