Skip to content

Commit

Permalink
feat: product warranties support (#1368)
Browse files Browse the repository at this point in the history
Co-authored-by: Silke <s.grueber@intershop.de>
  • Loading branch information
2 people authored and shauke committed Apr 25, 2024
1 parent 3acea81 commit bb952bd
Show file tree
Hide file tree
Showing 71 changed files with 1,391 additions and 80 deletions.
9 changes: 8 additions & 1 deletion e2e/cypress/e2e/pages/checkout/cart.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ export class CartPage {
.clear()
.wait(1000)
.type(num.toString())
.wait(1000),
.wait(1000)
.blur(),
get: () =>
cy
.get(this.tag)
Expand All @@ -107,6 +108,12 @@ export class CartPage {
.invoke('val')
.then(v => +v),
},
warranty: {
set: (warrantyId: string) => cy.get('[data-testing-id="product-warranties"]').eq(idx).select(warrantyId),
// Precondition for "get": all products except the last one has to have a warranty
// TODO: find a better way to detect if a line item has the warranty component
get: () => cy.get(this.tag).find('[data-testing-id="product-warranties"]').eq(idx),
},
remove: () => cy.get(this.tag).find('[data-testing-id="remove-line-item"]').eq(idx).click(),
sku: cy.get(this.tag).find('.product-id').eq(idx),
openVariationEditDialog: () =>
Expand Down
4 changes: 4 additions & 0 deletions e2e/cypress/e2e/pages/checkout/checkout-receipt.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ export class CheckoutReceiptPage {
get costCenterInformation() {
return cy.get('div[data-testing-id="buyer-cost-center"');
}

lineItemWarranty() {
return cy.get('ish-line-item-list-element').find('ish-line-item-warranty');
}
}
4 changes: 4 additions & 0 deletions e2e/cypress/e2e/pages/checkout/checkout-review.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ export class CheckoutReviewPage {
cy.get('button').contains('Submit order').click();
waitLoadingEnd(1000);
}

lineItemWarranty() {
return cy.get('ish-line-item-list-element').find('ish-line-item-warranty');
}
}
9 changes: 9 additions & 0 deletions e2e/cypress/e2e/pages/shopping/product-detail.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,13 @@ export class ProductDetailPage {
gotoMasterProduct() {
cy.get('a.all-variations-link').click();
}

get warranties() {
return cy.get('[data-testing-id="product-warranties"]');
}

selectWarranty(id: string) {
cy.get('[data-testing-id="product-warranties"]').find(`input[value="${id}"]`).check();
cy.wait(500);
}
}
166 changes: 166 additions & 0 deletions e2e/cypress/e2e/specs/shopping/product-warranty.b2c.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { at, waitLoadingEnd } from '../../framework';
import { createUserViaREST } from '../../framework/users';
import { LoginPage } from '../../pages/account/login.page';
import { MyAccountPage } from '../../pages/account/my-account.page';
import { sensibleDefaults } from '../../pages/account/registration.page';
import { CartPage } from '../../pages/checkout/cart.page';
import { CheckoutPaymentPage } from '../../pages/checkout/checkout-payment.page';
import { CheckoutReceiptPage } from '../../pages/checkout/checkout-receipt.page';
import { CheckoutReviewPage } from '../../pages/checkout/checkout-review.page';
import { HomePage } from '../../pages/home.page';
import { CategoryPage } from '../../pages/shopping/category.page';
import { FamilyPage } from '../../pages/shopping/family.page';
import { ProductDetailPage } from '../../pages/shopping/product-detail.page';

const _ = {
product1: {
sku: '5777062',
price: 614.0,
index: 0,
},
product2: {
sku: '4747809',
price: 513.25,
index: 1,
},
featuredProduct: {
sku: '201807171',
index: 2,
},
noWarrantyValue: '',
selectedWarranty: {
sku: '1YLEDTVSUP',
name: '1-year LED TV Support',
price: 100.0,
},
user: {
login: `test${new Date().getTime()}@testcity.de`,
...sensibleDefaults,
},
totalPrice: '1,327.25',
totalPrice2: '1,227.25',
catalog: 'Home-Entertainment',
category1: 'Home-Entertainment.220',
category2: 'Home-Entertainment.220.1584',
};

describe('Product Warranty B2C', () => {
describe('product with available warranties', () => {
before(() => ProductDetailPage.navigateTo(_.product1.sku));

it('displays available warranties', () => {
at(ProductDetailPage, page => page.warranties.should('exist'));
});

it('adds product with selected warranty to cart', () => {
at(ProductDetailPage, page => {
page.selectWarranty(_.selectedWarranty.sku);
page.addProductToCart().its('response.statusCode').should('equal', 201);
waitLoadingEnd();
page.header.miniCart.goToCart();
});
});

it('shows the selected warranty at the cart item', () => {
at(CartPage, page => {
page.lineItems.should('have.length', 1);
page.lineItem(_.product1.index).warranty.get().should('have.value', _.selectedWarranty.sku);
});
});

it('add product without warranty to cart', () => {
at(CartPage, page => page.header.gotoCategoryPage(_.catalog));
at(CategoryPage, page => page.gotoSubCategory(_.category1));
at(CategoryPage, page => page.gotoSubCategory(_.category2));
at(FamilyPage, page => page.productList.gotoProductDetailPageBySku(_.product2.sku));
at(ProductDetailPage, page => {
page.warranties.should('exist');
page.addProductToCart().its('response.statusCode').should('equal', 201);
waitLoadingEnd();
page.header.miniCart.goToCart();
});
});

it('product in cart should have a select-box with no selected warranty', () => {
at(CartPage, page => {
page.lineItems.should('have.length', 2);
page.lineItem(_.product2.index).warranty.get().should('have.value', _.noWarrantyValue);
});
});

it('changing a warranty should update the cart total', () => {
at(CartPage, page => {
page.lineItem(_.product2.index).warranty.set(_.selectedWarranty.sku);
waitLoadingEnd();
page.lineItem(_.product2.index).warranty.get().should('have.value', _.selectedWarranty.sku);
page.subtotal.should('contain', _.totalPrice);
});
});

it('removing a warranty should update the cart total', () => {
at(CartPage, page => {
page.lineItem(_.product1.index).warranty.set(_.noWarrantyValue);
waitLoadingEnd();
// doesn't work with current icm, doesn't change the total price when no warranty is selected
page.subtotal.should('contain', _.totalPrice2);
});
});
});

describe('product without avaiable warranties', () => {
it('does not show a warranty-select-box in the cart for this product', () => {
at(CartPage, page => page.header.gotoHomePage());
at(HomePage, page => page.gotoFeaturedProduct(_.featuredProduct.sku));
at(ProductDetailPage, page => {
page.addProductToCart().its('response.statusCode').should('equal', 201);
waitLoadingEnd();
page.header.miniCart.goToCart();
});
at(CartPage, page => {
page.lineItems.should('have.length', 3);
page.lineItem(_.featuredProduct.index).warranty.get().should('not.exist');
});
});
});

describe('checkout products as a logged-in user', () => {
before(() => createUserViaREST(_.user));

it('merges the existing cart items together with the selected warranties after logging in', () => {
at(CartPage, page => page.header.gotoLoginPage());
at(LoginPage, page => {
page.fillForm(_.user.login, _.user.password);
page.submit().its('response.statusCode').should('equal', 200);
waitLoadingEnd();
});
at(MyAccountPage, page => page.header.miniCart.goToCart());
at(CartPage, page => {
page.lineItems.should('have.length', 3);
// activate these tests if merge sorting issue has been fixed, see #92412
// page.lineItem(_.product1.index).warranty.get().should('have.value', _.noWarrantyValue);
// page.lineItem(_.product2.index).warranty.get().should('have.value', _.selectedWarranty.sku);
});
});

it('shows selected warranties on checkout review page', () => {
at(CartPage, page => page.beginCheckout());
at(CheckoutPaymentPage, page => {
page.selectPayment('INVOICE');
page.continueCheckout();
});
at(CheckoutReviewPage, page => {
page.lineItemWarranty().should('contain', _.selectedWarranty.name);
});
});

it('shows the selected warranties on checkout receipt page', () => {
at(CheckoutReviewPage, page => {
page.acceptTAC();
page.submitOrder();
});
at(CheckoutReceiptPage, page => {
page.lineItemWarranty().should('contain', _.selectedWarranty.name);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import { AddressData } from 'ish-core/models/address/address.interface';
import { BasketApprover } from 'ish-core/models/basket-approval/basket-approval.model';
import { BasketInfo } from 'ish-core/models/basket-info/basket-info.model';
import { BasketRebateData } from 'ish-core/models/basket-rebate/basket-rebate.interface';
import { BasketBaseData } from 'ish-core/models/basket/basket.interface';
import { LineItemData } from 'ish-core/models/line-item/line-item.interface';
import { PaymentInstrument } from 'ish-core/models/payment-instrument/payment-instrument.model';
import { PaymentMethodBaseData } from 'ish-core/models/payment-method/payment-method.interface';
import { PaymentData } from 'ish-core/models/payment/payment.interface';
import { BasketBaseData, BasketIncludedData } from 'ish-core/models/basket/basket.interface';
import { PriceData } from 'ish-core/models/price/price.interface';
import { ShippingMethodData } from 'ish-core/models/shipping-method/shipping-method.interface';
import { User } from 'ish-core/models/user/user.model';

import { RequisitionApproval, RequisitionUserBudget } from './requisition.model';
Expand All @@ -35,16 +28,6 @@ export interface RequisitionBaseData extends BasketBaseData {

export interface RequisitionData {
data: RequisitionBaseData | RequisitionBaseData[];
included?: {
invoiceToAddress?: { [urn: string]: AddressData };
lineItems?: { [id: string]: LineItemData };
discounts?: { [id: string]: BasketRebateData };
lineItems_discounts?: { [id: string]: BasketRebateData };
commonShipToAddress?: { [urn: string]: AddressData };
commonShippingMethod?: { [id: string]: ShippingMethodData };
payments?: { [id: string]: PaymentData };
payments_paymentMethod?: { [id: string]: PaymentMethodBaseData };
payments_paymentInstrument?: { [id: string]: PaymentInstrument };
};
included?: BasketIncludedData;
infos?: BasketInfo[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ type RequisitionIncludeType =
| 'commonShipToAddress'
| 'commonShippingMethod'
| 'discounts'
| 'lineItems_discounts'
| 'lineItems'
| 'lineItems_discounts'
| 'lineItems_warranty'
| 'payments'
| 'payments_paymentMethod'
| 'payments_paymentInstrument';
Expand All @@ -36,8 +37,9 @@ export class RequisitionsService {
'commonShipToAddress',
'commonShippingMethod',
'discounts',
'lineItems_discounts',
'lineItems',
'lineItems_discounts',
'lineItems_warranty',
'payments',
'payments_paymentMethod',
'payments_paymentInstrument',
Expand Down
4 changes: 4 additions & 0 deletions src/app/core/facades/checkout.facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ export class CheckoutFacade {
}
}

updateBasketItemWarranty(itemId: string, warrantySku: string) {
this.store.dispatch(updateBasketItem({ lineItemUpdate: { itemId, warrantySku } }));
}

updateBasketShippingMethod(shippingId: string) {
this.store.dispatch(updateBasketShippingMethod({ shippingId }));
}
Expand Down
9 changes: 8 additions & 1 deletion src/app/core/facades/product-context.facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ export interface ProductContext {
// child contexts
propagateActive: boolean;
children: Record<string | number, ProductContext>;

// selected warranty
selectedWarranty: string;
}

@Injectable()
Expand Down Expand Up @@ -502,7 +505,7 @@ export class ProductContextFacade extends RxState<ProductContext> implements OnD
items
.filter(x => !!x && !!x.quantity)
.forEach(child => {
this.shoppingFacade.addProductToBasket(child.sku, child.quantity);
this.shoppingFacade.addProductToBasket(child.sku, child.quantity, this.get('selectedWarranty'));
});
}

Expand All @@ -526,6 +529,10 @@ export class ProductContextFacade extends RxState<ProductContext> implements OnD
);
}

setSelectedWarranty(selectedWarranty: string) {
this.set('selectedWarranty', () => selectedWarranty);
}

ngOnDestroy(): void {
if (this.get('propagateActive')) {
this.set('propagateActive', () => false);
Expand Down
15 changes: 13 additions & 2 deletions src/app/core/facades/shopping.facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
} from 'ish-core/store/shopping/products';
import { getPromotion, getPromotions, loadPromotion } from 'ish-core/store/shopping/promotions';
import { getSearchTerm, getSuggestSearchResults, suggestSearch } from 'ish-core/store/shopping/search';
import { getWarranty, getWarrantyError, getWarrantyLoading, warrantyActions } from 'ish-core/store/shopping/warranties';
import { toObservable } from 'ish-core/utils/functions';
import { InjectSingle } from 'ish-core/utils/injection';
import { whenFalsy, whenTruthy } from 'ish-core/utils/operators';
Expand Down Expand Up @@ -148,8 +149,8 @@ export class ShoppingFacade {

// CHECKOUT

addProductToBasket(sku: string, quantity: number) {
this.store.dispatch(addProductToBasket({ sku, quantity }));
addProductToBasket(sku: string, quantity: number, warrantySku?: string) {
this.store.dispatch(addProductToBasket({ sku, quantity, warrantySku }));
}

// PRODUCT LISTING
Expand Down Expand Up @@ -240,4 +241,14 @@ export class ShoppingFacade {
});
return this.store.pipe(select(getPromotions(promotionIds)));
}

// WARRANTIES

warrantyById$(warrantyId: string) {
this.store.dispatch(warrantyActions.loadWarranty({ warrantyId }));
return this.store.pipe(select(getWarranty(warrantyId)));
}

warrantyError$ = this.store.pipe(select(getWarrantyError));
warrantyLoading$ = this.store.pipe(select(getWarrantyLoading));
}
1 change: 1 addition & 0 deletions src/app/core/models/basket-merge/basket-merge.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class BasketMergeHelper {
lineItems: basketMergeData.included.targetBasket_lineItems || undefined,
discounts: basketMergeData.included.targetBasket_discounts || undefined,
lineItems_discounts: basketMergeData.included.targetBasket_lineItems_discounts || undefined,
lineItems_warranty: basketMergeData.included.targetBasket_lineItems_warranty || undefined,
commonShipToAddress: basketMergeData.included.targetBasket_commonShipToAddress || undefined,
commonShippingMethod: basketMergeData.included.targetBasket_commonShippingMethod || undefined,
payments: basketMergeData.included.targetBasket_payments || undefined,
Expand Down
4 changes: 3 additions & 1 deletion src/app/core/models/basket-merge/basket-merge.interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AddressData } from 'ish-core/models/address/address.interface';
import { BasketRebateData } from 'ish-core/models/basket-rebate/basket-rebate.interface';
import { BasketWarrantyData } from 'ish-core/models/basket-warranty/basket-warranty.interface';
import { BasketBaseData } from 'ish-core/models/basket/basket.interface';
import { LineItemData } from 'ish-core/models/line-item/line-item.interface';
import { PaymentInstrument } from 'ish-core/models/payment-instrument/payment-instrument.model';
Expand All @@ -18,8 +19,9 @@ export interface BasketMergeData {
targetBasket: { [id: string]: BasketBaseData };
targetBasket_invoiceToAddress?: { [urn: string]: AddressData };
targetBasket_lineItems?: { [id: string]: LineItemData };
targetBasket_discounts?: { [id: string]: BasketRebateData };
targetBasket_lineItems_warranty?: { [id: string]: BasketWarrantyData };
targetBasket_lineItems_discounts?: { [id: string]: BasketRebateData };
targetBasket_discounts?: { [id: string]: BasketRebateData };
targetBasket_commonShipToAddress?: { [urn: string]: AddressData };
targetBasket_commonShippingMethod?: { [id: string]: ShippingMethodData };
targetBasket_payments?: { [id: string]: PaymentData };
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { PriceItemData } from 'ish-core/models/price-item/price-item.interface';

export interface BasketWarrantyData {
product: string;
price: PriceItemData;
}
Loading

0 comments on commit bb952bd

Please sign in to comment.