Skip to content

Commit

Permalink
feat(seo): optimized product URLs with full category path (#1164)
Browse files Browse the repository at this point in the history
* still including product name and variation attributes
* changed product sku marker from 'sku' to 'prd'

BREAKING CHANGES: Changed product routes/URLs (see [Migrations / 2.4 to 3.0](https://github.com/intershop/intershop-pwa/blob/develop/docs/guides/migrations.md#24-to-30) for more details).
  • Loading branch information
Eisie96 authored and shauke committed Jul 19, 2022
1 parent 2a212bb commit 0252857
Show file tree
Hide file tree
Showing 19 changed files with 145 additions and 114 deletions.
4 changes: 2 additions & 2 deletions e2e/cypress/integration/pages/shopping/product-detail.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ export class ProductDetailPage {

static navigateTo(sku: string, categoryUniqueId?: string) {
if (categoryUniqueId) {
cy.visit(`/sku${sku}-ctg${categoryUniqueId}`);
cy.visit(`/prd${sku}-ctg${categoryUniqueId}`);
} else {
cy.visit(`/sku${sku}`);
cy.visit(`/prd${sku}`);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('Page Meta', () => {
at(ProductDetailPage, page => {
page.metaData.check({
title: 'Google Home - Smart Home | Intershop PWA',
url: /.*\/smart-home\/Google-Home-sku201807171-ctgHome-Entertainment.SmartHome$/,
url: /.*\/home-entertainment\/smart-home\/google-home-prd201807171-ctgHome-Entertainment.SmartHome$/,
description: 'Google Home - Hands-free help from the Google Assistant',
'og:image': /.*201807171_front.*/,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('Page Meta', () => {
at(ProductDetailPage, page => {
page.metaData.check({
title: 'Kodak Slice - Digital Cameras | Intershop PWA',
url: /.*\/digital-cameras\/Kodak-Slice-sku3957284-ctgCameras-Camcorders.575$/,
url: /.*\/digital-cameras\/kodak-slice-prd3957284-ctgCameras-Camcorders.575$/,
description: 'Kodak Slice - Slice - 14MP, 5x optisch, 16:9, nickel',
'og:image': /.*3957284-5640.jpg.*/,
});
Expand Down
6 changes: 3 additions & 3 deletions e2e/test-universal.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ universalTest 10 "${PWA_BASE_URL}/home" "<ish-content-include includeid=.include
universalTest 11 "${PWA_BASE_URL}/home" "<link rel=.canonical. href=.${PWA_CANONICAL_BASE_URL}/home/.>"
universalTest 12 "${PWA_BASE_URL}/home" "<meta property=.og:image. content=./assets/img/og-image-default"
universalTest 13 "${PWA_BASE_URL}/home" "<title>inTRONICS Home | Intershop PWA</title>"
universalTest 14 "${PWA_BASE_URL}/sku6997041" "<link rel=.canonical. href=.${PWA_CANONICAL_BASE_URL}/notebooks/Asus-Eee-PC-1008P-Karim-Rashid-sku6997041-ctgComputers.1835.151.>"
universalTest 15 "${PWA_BASE_URL}/sku6997041" "<meta property=.og:image. content=[^>]*6997041"
universalTest 16 "${PWA_BASE_URL}/sku6997041" "<title>Asus Eee PC 1008P .Karim Rashid. [^>]* | Intershop PWA</title>"
universalTest 14 "${PWA_BASE_URL}/prd6997041" "<link rel=.canonical. href=.${PWA_CANONICAL_BASE_URL}/computers/notebooks-and-pcs/notebooks/asus-eee-pc-1008p-karim-rashid-prd6997041-ctgComputers.1835.151.>"
universalTest 15 "${PWA_BASE_URL}/prd6997041" "<meta property=.og:image. content=[^>]*6997041"
universalTest 16 "${PWA_BASE_URL}/prd6997041" "<title>Asus Eee PC 1008P .Karim Rashid. [^>]* | Intershop PWA</title>"
universalTest 17 "${PWA_BASE_URL}/home;device=tablet" "class=.header container tablet"
universalTest 18 "${PWA_BASE_URL}/home;device=desktop" "class=.header container desktop"
universalTest 19 "${PWA_BASE_URL}/home;device=mobile" "class=.header container mobile"
Expand Down
2 changes: 1 addition & 1 deletion src/app/core/directives/server-html.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('Server Html Directive', () => {
it('should transform the given links to routing links', () => {
expect(element).toMatchInlineSnapshot(`
<div>
<div><a href="/sku8182790134362">Product</a></div>
<div><a href="/product/8182790134362">Product</a></div>
<div><a href="http://google.de">Google</a></div>
<div><a href="/basket">Basket</a></div>
</div>
Expand Down
35 changes: 25 additions & 10 deletions src/app/core/facades/product-context.facade.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ describe('Product Context Facade', () => {
"loading": false,
"maxQuantity": 100,
"minQuantity": 10,
"productURL": "/sku123",
"productURL": "/prd123",
"propagateActive": true,
"quantity": 10,
"quantityError": undefined,
Expand Down Expand Up @@ -183,7 +183,7 @@ describe('Product Context Facade', () => {
"loading": false,
"maxQuantity": 100,
"minQuantity": 10,
"productURL": "/sku123",
"productURL": "/prd123",
"propagateActive": true,
"quantity": 10,
"quantityError": undefined,
Expand Down Expand Up @@ -385,8 +385,14 @@ describe('Product Context Facade', () => {
beforeEach(() => {
product = {
sku: '123',
name: '123',
completenessLevel: ProductCompletenessLevel.Detail,
defaultCategory: { uniqueId: 'ABC' } as Category,
defaultCategory: {
uniqueId: 'ABC',
name: 'ABC',
pathElements: [{ uniqueId: 'ABC', name: 'ABC' } as Category],
} as CategoryView,
defaultCategoryId: 'ABC',
} as ProductView;

when(shoppingFacade.product$(anything(), anything())).thenReturn(of(product));
Expand All @@ -395,7 +401,7 @@ describe('Product Context Facade', () => {
});

it('should calculate the url property of the product with default category', () => {
expect(context.get('productURL')).toMatchInlineSnapshot(`"//sku123-ctgABC"`);
expect(context.get('productURL')).toMatchInlineSnapshot(`"/abc/123-prd123-ctgABC"`);
});
});

Expand All @@ -405,18 +411,21 @@ describe('Product Context Facade', () => {
beforeEach(() => {
product = {
sku: '123',
name: '123',
completenessLevel: ProductCompletenessLevel.Detail,
} as ProductView;

when(shoppingFacade.product$(anything(), anything())).thenReturn(of(product));
when(shoppingFacade.category$(anything())).thenReturn(of({ uniqueId: 'ASDF' } as CategoryView));
when(shoppingFacade.category$(anything())).thenReturn(
of({ uniqueId: 'ASDF', pathElements: [{ uniqueId: 'ASDF', name: 'ASDF' } as Category] } as CategoryView)
);

context.set('categoryId', () => 'ASDF');
context.set('sku', () => '123');
});

it('should calculate the url property of the product with context category', () => {
expect(context.get('productURL')).toMatchInlineSnapshot(`"//sku123-ctgASDF"`);
expect(context.get('productURL')).toMatchInlineSnapshot(`"/asdf/123-prd123-ctgASDF"`);
});
});

Expand All @@ -426,19 +435,25 @@ describe('Product Context Facade', () => {
beforeEach(() => {
product = {
sku: '123',
name: 'abc123',
completenessLevel: ProductCompletenessLevel.Detail,
defaultCategory: { uniqueId: 'ABC' } as Category,
defaultCategory: {
uniqueId: 'ABC',
name: 'ABC',
pathElements: [{ uniqueId: 'ABC', name: 'ABC' } as Category],
} as CategoryView,
} as ProductView;

when(shoppingFacade.product$(anything(), anything())).thenReturn(of(product));
when(shoppingFacade.category$(anything())).thenReturn(of({ uniqueId: 'ASDF' } as CategoryView));

when(shoppingFacade.category$(anything())).thenReturn(
of({ uniqueId: 'ASDF', pathElements: [{ uniqueId: 'ASDF', name: 'ASDF' } as Category] } as CategoryView)
);
context.set('categoryId', () => 'ASDF');
context.set('sku', () => '123');
});

it('should calculate the url property of the product with context category', () => {
expect(context.get('productURL')).toMatchInlineSnapshot(`"//sku123-ctgASDF"`);
expect(context.get('productURL')).toMatchInlineSnapshot(`"/asdf/abc123-prd123-ctgASDF"`);
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('Content View Helper', () => {
it.each([
['route://category/Computers', '/category/Computers'],
['route://category/Home-Entertainment.SmartHome', '/category/Home-Entertainment.SmartHome'],
['product://201807195@inSPIRED-inTRONICS', '/sku201807195'],
['product://201807195@inSPIRED-inTRONICS', '/product/201807195'],
])(`should transform %s to %s`, (input, expected) => {
const pagelet = createContentPageletView({
definitionQualifiedName: 'fq',
Expand Down
4 changes: 2 additions & 2 deletions src/app/core/models/product-view/product-view.model.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Category } from 'ish-core/models/category/category.model';
import { CategoryView } from 'ish-core/models/category-view/category-view.model';
import { Product } from 'ish-core/models/product/product.model';

import { createProductView } from './product-view.model';
Expand All @@ -25,7 +25,7 @@ describe('Product View Model', () => {
uniqueId: '123',
name: 'test',
categoryPath: ['123'],
} as Category;
} as CategoryView;

const view = createProductView({ sku: 'some', defaultCategoryId: '123' } as Product, category);

Expand Down
10 changes: 5 additions & 5 deletions src/app/core/models/product-view/product-view.model.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Category } from 'ish-core/models/category/category.model';
import { CategoryView } from 'ish-core/models/category-view/category-view.model';
import { Product, VariationProduct, VariationProductMaster } from 'ish-core/models/product/product.model';

export type ProductView = Partial<SimpleProductView> &
Partial<Omit<VariationProductView, 'type'>> &
Partial<Omit<VariationProductMasterView, 'type'>>;

interface SimpleProductView extends Product {
defaultCategory: Category;
defaultCategory: CategoryView;
}

interface VariationProductView extends VariationProduct, SimpleProductView {
Expand All @@ -21,7 +21,7 @@ interface VariationProductMasterView extends VariationProductMaster, SimpleProdu
defaultVariationSKU: string;
}

export function createProductView(product: Product, defaultCategory?: Category): SimpleProductView {
export function createProductView(product: Product, defaultCategory?: CategoryView): SimpleProductView {
return (
product && {
...product,
Expand All @@ -34,7 +34,7 @@ export function createVariationProductMasterView(
product: VariationProductMaster,
defaultVariationSKU: string,
variations: VariationProduct[],
defaultCategory?: Category
defaultCategory?: CategoryView
): VariationProductMasterView {
return (
product &&
Expand All @@ -51,7 +51,7 @@ export function createVariationProductView(
product: VariationProduct,
variations: VariationProduct[],
productMaster: VariationProductMaster,
defaultCategory?: Category
defaultCategory?: CategoryView
): VariationProductView {
return (
product &&
Expand Down
4 changes: 2 additions & 2 deletions src/app/core/routing/product/product-route.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Pipe, PipeTransform } from '@angular/core';

import { Category } from 'ish-core/models/category/category.model';
import { CategoryView } from 'ish-core/models/category-view/category-view.model';
import { ProductView } from 'ish-core/models/product-view/product-view.model';
import { generateProductUrl } from 'ish-core/routing/product/product.route';

@Pipe({ name: 'ishProductRoute', pure: true })
export class ProductRoutePipe implements PipeTransform {
transform(product: ProductView, category?: Category): string {
transform(product: ProductView, category?: CategoryView): string {
return generateProductUrl(product, category);
}
}
Loading

0 comments on commit 0252857

Please sign in to comment.