Compare commits
2 Commits
feat/mater
...
1b7c0c48e7
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b7c0c48e7 | |||
|
|
cb86137730 |
@@ -97,7 +97,9 @@ describe('ProductDetailComponent', () => {
|
|||||||
const languageService = {
|
const languageService = {
|
||||||
currentLang,
|
currentLang,
|
||||||
selectedLang: () => currentLang(),
|
selectedLang: () => currentLang(),
|
||||||
setLocalizedRouteOverrides: jasmine.createSpy('setLocalizedRouteOverrides'),
|
setLocalizedRouteOverrides: jasmine.createSpy(
|
||||||
|
'setLocalizedRouteOverrides',
|
||||||
|
),
|
||||||
clearLocalizedRouteOverrides: jasmine.createSpy(
|
clearLocalizedRouteOverrides: jasmine.createSpy(
|
||||||
'clearLocalizedRouteOverrides',
|
'clearLocalizedRouteOverrides',
|
||||||
),
|
),
|
||||||
@@ -113,7 +115,9 @@ describe('ProductDetailComponent', () => {
|
|||||||
.createSpy('quantityForVariant')
|
.createSpy('quantityForVariant')
|
||||||
.and.returnValue(0),
|
.and.returnValue(0),
|
||||||
loadCart: jasmine.createSpy('loadCart').and.returnValue(of(null)),
|
loadCart: jasmine.createSpy('loadCart').and.returnValue(of(null)),
|
||||||
resolveMediaUrl: jasmine.createSpy('resolveMediaUrl').and.returnValue(null),
|
resolveMediaUrl: jasmine
|
||||||
|
.createSpy('resolveMediaUrl')
|
||||||
|
.and.returnValue(null),
|
||||||
};
|
};
|
||||||
|
|
||||||
const router = {
|
const router = {
|
||||||
@@ -126,9 +130,13 @@ describe('ProductDetailComponent', () => {
|
|||||||
} as unknown as Router;
|
} as unknown as Router;
|
||||||
|
|
||||||
const activatedRoute = {
|
const activatedRoute = {
|
||||||
paramMap: of(convertToParamMap({ productSlug: '91823f84-bike-wall-hanger' })),
|
paramMap: of(
|
||||||
|
convertToParamMap({ productSlug: '91823f84-bike-wall-hanger' }),
|
||||||
|
),
|
||||||
snapshot: {
|
snapshot: {
|
||||||
paramMap: convertToParamMap({ productSlug: '91823f84-bike-wall-hanger' }),
|
paramMap: convertToParamMap({
|
||||||
|
productSlug: '91823f84-bike-wall-hanger',
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
} as unknown as ActivatedRoute;
|
} as unknown as ActivatedRoute;
|
||||||
|
|
||||||
@@ -200,7 +208,9 @@ describe('ProductDetailComponent', () => {
|
|||||||
it('builds a soft SSR fallback with 200 + index follow', () => {
|
it('builds a soft SSR fallback with 200 + index follow', () => {
|
||||||
const { component, seoService, responseInit } = createComponent();
|
const { component, seoService, responseInit } = createComponent();
|
||||||
|
|
||||||
expect((component as any).shouldUseSoftSeoFallback({ status: 500 })).toBeTrue();
|
expect(
|
||||||
|
(component as any).shouldUseSoftSeoFallback({ status: 500 }),
|
||||||
|
).toBeTrue();
|
||||||
(component as any).setResponseStatus(200);
|
(component as any).setResponseStatus(200);
|
||||||
(component as any).applySoftFallbackSeo('91823f84-bike-wall-hanger');
|
(component as any).applySoftFallbackSeo('91823f84-bike-wall-hanger');
|
||||||
|
|
||||||
@@ -221,7 +231,9 @@ describe('ProductDetailComponent', () => {
|
|||||||
it('keeps hard fallback noindex for missing products', () => {
|
it('keeps hard fallback noindex for missing products', () => {
|
||||||
const { component, seoService, responseInit } = createComponent();
|
const { component, seoService, responseInit } = createComponent();
|
||||||
|
|
||||||
expect((component as any).shouldUseSoftSeoFallback({ status: 404 })).toBeFalse();
|
expect(
|
||||||
|
(component as any).shouldUseSoftSeoFallback({ status: 404 }),
|
||||||
|
).toBeFalse();
|
||||||
(component as any).setResponseStatus(404);
|
(component as any).setResponseStatus(404);
|
||||||
(component as any).applyHardFallbackSeo();
|
(component as any).applyHardFallbackSeo();
|
||||||
|
|
||||||
|
|||||||
@@ -254,37 +254,35 @@ export class ProductDetailComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const productSlug = routeParams.productSlug as string;
|
const productSlug = routeParams.productSlug as string;
|
||||||
return this.shopService
|
return this.shopService.getProductByPublicPath(productSlug).pipe(
|
||||||
.getProductByPublicPath(productSlug)
|
catchError((error) => {
|
||||||
.pipe(
|
this.languageService.clearLocalizedRouteOverrides();
|
||||||
catchError((error) => {
|
this.product.set(null);
|
||||||
this.languageService.clearLocalizedRouteOverrides();
|
this.selectedVariantId.set(null);
|
||||||
this.product.set(null);
|
this.setSelectedImageAssetId(null);
|
||||||
this.selectedVariantId.set(null);
|
this.modelFile.set(null);
|
||||||
this.setSelectedImageAssetId(null);
|
const isNotFound = error?.status === 404;
|
||||||
this.modelFile.set(null);
|
if (isNotFound) {
|
||||||
const isNotFound = error?.status === 404;
|
this.error.set('SHOP.NOT_FOUND');
|
||||||
if (isNotFound) {
|
this.setResponseStatus(404);
|
||||||
this.error.set('SHOP.NOT_FOUND');
|
this.applyHardFallbackSeo();
|
||||||
this.setResponseStatus(404);
|
|
||||||
this.applyHardFallbackSeo();
|
|
||||||
return of(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.shouldUseSoftSeoFallback(error)) {
|
|
||||||
this.error.set(null);
|
|
||||||
this.softFallbackActive.set(true);
|
|
||||||
this.setResponseStatus(200);
|
|
||||||
this.applySoftFallbackSeo(productSlug);
|
|
||||||
return of(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.error.set('SHOP.LOAD_ERROR');
|
|
||||||
this.setResponseStatus(503);
|
|
||||||
return of(null);
|
return of(null);
|
||||||
}),
|
}
|
||||||
finalize(() => this.loading.set(false)),
|
|
||||||
);
|
if (this.shouldUseSoftSeoFallback(error)) {
|
||||||
|
this.error.set(null);
|
||||||
|
this.softFallbackActive.set(true);
|
||||||
|
this.setResponseStatus(200);
|
||||||
|
this.applySoftFallbackSeo(productSlug);
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.error.set('SHOP.LOAD_ERROR');
|
||||||
|
this.setResponseStatus(503);
|
||||||
|
return of(null);
|
||||||
|
}),
|
||||||
|
finalize(() => this.loading.set(false)),
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
takeUntilDestroyed(this.destroyRef),
|
takeUntilDestroyed(this.destroyRef),
|
||||||
)
|
)
|
||||||
@@ -904,9 +902,7 @@ export class ProductDetailComponent {
|
|||||||
return this.normalizeRouteParam(this.route.snapshot.paramMap.get(name));
|
return this.normalizeRouteParam(this.route.snapshot.paramMap.get(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
private normalizeRouteParam(
|
private normalizeRouteParam(value: string | null | undefined): string | null {
|
||||||
value: string | null | undefined,
|
|
||||||
): string | null {
|
|
||||||
const normalized = String(value ?? '').trim();
|
const normalized = String(value ?? '').trim();
|
||||||
return normalized || null;
|
return normalized || null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,24 +60,26 @@ describe('ShopPageComponent', () => {
|
|||||||
'TranslateService',
|
'TranslateService',
|
||||||
['instant'],
|
['instant'],
|
||||||
);
|
);
|
||||||
translate.instant.and.callFake((key: string, params?: { count?: number }) => {
|
translate.instant.and.callFake(
|
||||||
const translations: Record<string, string> = {
|
(key: string, params?: { count?: number }) => {
|
||||||
'SHOP.TITLE': 'Technische Lösungen',
|
const translations: Record<string, string> = {
|
||||||
'SHOP.SUBTITLE': 'Fertige Produkte, die praktische Probleme lösen',
|
'SHOP.TITLE': 'Technische Lösungen',
|
||||||
'SHOP.CATALOG_TITLE': 'Alle Produkte',
|
'SHOP.SUBTITLE': 'Fertige Produkte, die praktische Probleme lösen',
|
||||||
'SHOP.CATALOG_LABEL': 'Katalog',
|
'SHOP.CATALOG_TITLE': 'Alle Produkte',
|
||||||
'SHOP.SELECTED_CATEGORY': 'Ausgewählte Kategorie',
|
'SHOP.CATALOG_LABEL': 'Katalog',
|
||||||
'SHOP.CATALOG_META_DESCRIPTION':
|
'SHOP.SELECTED_CATEGORY': 'Ausgewählte Kategorie',
|
||||||
'Entdecken Sie 3D-gedruckte Produkte und technisches Zubehör.',
|
'SHOP.CATALOG_META_DESCRIPTION':
|
||||||
'SEO.ROUTES.SHOP.CATEGORY_TITLE': 'Shop-Kategorie | 3D fab',
|
'Entdecken Sie 3D-gedruckte Produkte und technisches Zubehör.',
|
||||||
'SEO.ROUTES.SHOP.CATEGORY_DESCRIPTION':
|
'SEO.ROUTES.SHOP.CATEGORY_TITLE': 'Shop-Kategorie | 3D fab',
|
||||||
'Entdecken Sie Produkte dieser Kategorie und technische Lösungen.',
|
'SEO.ROUTES.SHOP.CATEGORY_DESCRIPTION':
|
||||||
};
|
'Entdecken Sie Produkte dieser Kategorie und technische Lösungen.',
|
||||||
if (key === 'SHOP.CATEGORY_META') {
|
};
|
||||||
return `${params?.count ?? 0} Produkte in dieser Kategorie verfügbar`;
|
if (key === 'SHOP.CATEGORY_META') {
|
||||||
}
|
return `${params?.count ?? 0} Produkte in dieser Kategorie verfügbar`;
|
||||||
return translations[key] ?? key;
|
}
|
||||||
});
|
return translations[key] ?? key;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const currentLang = signal<'it' | 'en' | 'de' | 'fr'>('de');
|
const currentLang = signal<'it' | 'en' | 'de' | 'fr'>('de');
|
||||||
const languageService = {
|
const languageService = {
|
||||||
@@ -100,11 +102,17 @@ describe('ShopPageComponent', () => {
|
|||||||
flattenCategoryTree: jasmine
|
flattenCategoryTree: jasmine
|
||||||
.createSpy('flattenCategoryTree')
|
.createSpy('flattenCategoryTree')
|
||||||
.and.returnValue([]),
|
.and.returnValue([]),
|
||||||
quantityForProduct: jasmine.createSpy('quantityForProduct').and.returnValue(0),
|
quantityForProduct: jasmine
|
||||||
|
.createSpy('quantityForProduct')
|
||||||
|
.and.returnValue(0),
|
||||||
loadCart: jasmine.createSpy('loadCart').and.returnValue(of(null)),
|
loadCart: jasmine.createSpy('loadCart').and.returnValue(of(null)),
|
||||||
clearCart: jasmine.createSpy('clearCart').and.returnValue(of(null)),
|
clearCart: jasmine.createSpy('clearCart').and.returnValue(of(null)),
|
||||||
removeCartItem: jasmine.createSpy('removeCartItem').and.returnValue(of(null)),
|
removeCartItem: jasmine
|
||||||
updateCartItem: jasmine.createSpy('updateCartItem').and.returnValue(of(null)),
|
.createSpy('removeCartItem')
|
||||||
|
.and.returnValue(of(null)),
|
||||||
|
updateCartItem: jasmine
|
||||||
|
.createSpy('updateCartItem')
|
||||||
|
.and.returnValue(of(null)),
|
||||||
};
|
};
|
||||||
|
|
||||||
const router = {
|
const router = {
|
||||||
@@ -164,7 +172,9 @@ describe('ShopPageComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('keeps noindex for categories explicitly marked as non-indexable', () => {
|
it('keeps noindex for categories explicitly marked as non-indexable', () => {
|
||||||
const { component, seoService } = createComponent('/de/shop/compatible-with-garmin');
|
const { component, seoService } = createComponent(
|
||||||
|
'/de/shop/compatible-with-garmin',
|
||||||
|
);
|
||||||
|
|
||||||
(component as any).applySeo(buildCategory({ indexable: false }));
|
(component as any).applySeo(buildCategory({ indexable: false }));
|
||||||
|
|
||||||
@@ -180,7 +190,9 @@ describe('ShopPageComponent', () => {
|
|||||||
'/de/shop/compatible-with-garmin',
|
'/de/shop/compatible-with-garmin',
|
||||||
);
|
);
|
||||||
|
|
||||||
expect((component as any).shouldUseSoftSeoFallback({ status: 500 })).toBeTrue();
|
expect(
|
||||||
|
(component as any).shouldUseSoftSeoFallback({ status: 500 }),
|
||||||
|
).toBeTrue();
|
||||||
(component as any).setResponseStatus(200);
|
(component as any).setResponseStatus(200);
|
||||||
(component as any).applySoftFallbackSeo('compatible-with-garmin');
|
(component as any).applySoftFallbackSeo('compatible-with-garmin');
|
||||||
|
|
||||||
@@ -203,7 +215,9 @@ describe('ShopPageComponent', () => {
|
|||||||
'/de/shop/compatible-with-garmin',
|
'/de/shop/compatible-with-garmin',
|
||||||
);
|
);
|
||||||
|
|
||||||
expect((component as any).shouldUseSoftSeoFallback({ status: 404 })).toBeFalse();
|
expect(
|
||||||
|
(component as any).shouldUseSoftSeoFallback({ status: 404 }),
|
||||||
|
).toBeFalse();
|
||||||
(component as any).setResponseStatus(404);
|
(component as any).setResponseStatus(404);
|
||||||
(component as any).applyHardErrorSeo();
|
(component as any).applyHardErrorSeo();
|
||||||
|
|
||||||
|
|||||||
@@ -528,9 +528,7 @@ export class ShopPageComponent {
|
|||||||
return this.normalizeRouteParam(this.route.snapshot.paramMap.get(name));
|
return this.normalizeRouteParam(this.route.snapshot.paramMap.get(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
private normalizeRouteParam(
|
private normalizeRouteParam(value: string | null | undefined): string | null {
|
||||||
value: string | null | undefined,
|
|
||||||
): string | null {
|
|
||||||
const normalized = String(value ?? '').trim();
|
const normalized = String(value ?? '').trim();
|
||||||
return normalized || null;
|
return normalized || null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user