Skip to content

Commit

Permalink
feat(category)!: create breadcrumbs and add createTree method (#2629)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `DaffCategoryFactory` now requires `DaffCategoryBreadcrumbFactory` as a constructor argument. It is recommended to DI the factory
  • Loading branch information
griest024 committed Nov 30, 2023
1 parent bcab5c9 commit 556e69c
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import { MagentoProduct } from '@daffodil/product/driver/magento';

import { DaffMagentoCategoryTransformerService } from './category-transformer.service';

describe('DaffMagentoCategoryTransformerService', () => {

describe('@daffodil/category/driver/magento | DaffMagentoCategoryTransformerService', () => {
let service: DaffMagentoCategoryTransformerService;
let categoryFactory: DaffCategoryFactory;
let stubCategory: DaffCategory;
Expand Down Expand Up @@ -107,7 +106,16 @@ describe('DaffMagentoCategoryTransformerService', () => {
});

it('should return a DaffCategory', () => {
expect(result).toEqual(stubCategory);
expect(result.id).toEqual(stubCategory.id);
expect(result.name).toEqual(stubCategory.name);
expect(result.url).toEqual(stubCategory.url);
expect(result.canonicalUrl).toEqual(stubCategory.canonicalUrl);
expect(result.children_count).toEqual(stubCategory.children_count);
expect(result.description).toEqual(stubCategory.description);
expect(result.meta_description).toEqual(stubCategory.meta_description);
expect(result.meta_title).toEqual(stubCategory.meta_title);
expect(result.product_ids).toEqual(jasmine.arrayContaining(mockProducts.map(({ sku }) => sku)));
expect(result.total_products).toEqual(mockProducts.length);
});

it('should return breadcrumbs in order of category_level', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { TestBed } from '@angular/core/testing';

import { DaffCategory } from '@daffodil/category';
import {
DaffCategoryPageLoadSuccess,
Expand All @@ -8,12 +10,11 @@ import { DaffCategoryFactory } from '@daffodil/category/testing';
import { daffCategoryEntitiesAdapter } from './category-entities-adapter';
import { daffCategoryEntitiesReducer } from './category-entities.reducer';

describe('Category | Category Entities Reducer', () => {

describe('@daffodil/category/state | daffCategoryEntitiesReducer', () => {
let categoryFactory: DaffCategoryFactory;

beforeEach(() => {
categoryFactory = new DaffCategoryFactory();
categoryFactory = TestBed.inject(DaffCategoryFactory);
});

describe('when an unknown action is triggered', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { TestBed } from '@angular/core/testing';

import { DaffCategoryBreadcrumb } from '@daffodil/category';

import { DaffCategoryBreadcrumbFactory } from './category-breadcrumb.factory';

describe('@daffodil/category/testing | DaffCategoryBreadcrumbFactory', () => {
let factory: DaffCategoryBreadcrumbFactory;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [DaffCategoryBreadcrumbFactory],
});

factory = TestBed.inject(DaffCategoryBreadcrumbFactory);
});

it('should be created', () => {
expect(factory).toBeTruthy();
});

describe('create', () => {

let result: DaffCategoryBreadcrumb;

beforeEach(() => {
result = factory.create();
});

it('should return a Category with all required fields defined', () => {
expect(result.id).toBeDefined();
expect(result.url).toBeDefined();
expect(result.name).toBeDefined();
expect(result.level).toBeDefined();
});
});
});
24 changes: 24 additions & 0 deletions libs/category/testing/src/factories/category-breadcrumb.factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Injectable } from '@angular/core';
import { faker } from '@faker-js/faker/locale/en_US';

import { DaffCategoryBreadcrumb } from '@daffodil/category';
import { DaffModelFactory } from '@daffodil/core/testing';

export class MockCategoryBreadcrumb implements DaffCategoryBreadcrumb {
id = faker.datatype.uuid();
name = faker.commerce.productMaterial();
level = faker.datatype.number({ min: 1, max: 5 });
url = faker.commerce.productMaterial();
}

/**
* A factory for creating a {@link DaffCategoryBreadcrumb}.
*/
@Injectable({
providedIn: 'root',
})
export class DaffCategoryBreadcrumbFactory extends DaffModelFactory<DaffCategoryBreadcrumb, typeof MockCategoryBreadcrumb>{
constructor() {
super(MockCategoryBreadcrumb);
}
}
69 changes: 60 additions & 9 deletions libs/category/testing/src/factories/category.factory.spec.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
import { TestBed } from '@angular/core/testing';

import { DaffCategory } from '@daffodil/category';
import { DaffProduct } from '@daffodil/product';

import { DaffCategoryFactory } from './category.factory';

describe('Category | Testing | Factories | DaffCategoryFactory', () => {

let categoryFactory;
describe('@daffodil/category/testing | DaffCategoryFactory', () => {
let factory: DaffCategoryFactory;
let result: DaffCategory;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [DaffCategoryFactory],
});

categoryFactory = TestBed.inject(DaffCategoryFactory);
factory = TestBed.inject(DaffCategoryFactory);
});

it('should be created', () => {
expect(categoryFactory).toBeTruthy();
expect(factory).toBeTruthy();
});

describe('create', () => {

let result: DaffCategory;

beforeEach(() => {
result = categoryFactory.create();
result = factory.create();
});

it('should return a Category with all required fields defined', () => {
Expand All @@ -46,4 +44,57 @@ describe('Category | Testing | Factories | DaffCategoryFactory', () => {
expect(result.url[0]).toEqual('/');
});
});

describe('createTree', () => {
describe('when depth is 0', () => {
beforeEach(() => {
result = factory.createTree(0);
});

it('should create no children', () => {
expect(result.children?.length).toBeFalsy();
});
});

describe('when depth is 2', () => {
beforeEach(() => {
result = factory.createTree(2);
});

it('should create children of children', () => {
expect(result.children?.length).toBeGreaterThan(0);
result.children.forEach((child) => {
expect(child.children?.length).toBeGreaterThan(0);
child.children.forEach((inner) => {
expect(inner.children?.length).toBeFalsy();
});
});
});
});

describe('when product IDs are specified', () => {
let productIds: DaffProduct['id'][];

beforeEach(() => {
productIds = ['0', '1', '2', '3'];
result = factory.createTree(1, productIds);
});

it('should set product fields from the IDs', () => {
expect(result.product_ids).toEqual(productIds);
expect(result.total_products).toEqual(productIds.length);
});

it('should set child product fields from a subset, no smaller than half of the previous set, of the passed IDs', () => {
result.children.forEach((child) => {
child.product_ids.forEach((id) => {
expect(productIds).toContain(id);
});
expect(child.product_ids.length).toBeGreaterThanOrEqual(productIds.length / 2);
expect(child.product_ids.length).toBeLessThanOrEqual(productIds.length);
expect(child.total_products).toEqual(child.product_ids.length);
});
});
});
});
});
67 changes: 52 additions & 15 deletions libs/category/testing/src/factories/category.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,28 @@ import { Injectable } from '@angular/core';
import { faker } from '@faker-js/faker/locale/en_US';

import { DaffCategory } from '@daffodil/category';
import { randomSubset } from '@daffodil/core';
import { DaffModelFactory } from '@daffodil/core/testing';
import { DaffProduct } from '@daffodil/product';

import { DaffCategoryBreadcrumbFactory } from './category-breadcrumb.factory';

export class MockCategory implements DaffCategory {
id = faker.datatype.uuid();
url = `/${faker.internet.domainWord()}.html`;
url = `/${faker.helpers.unique(faker.internet.domainWord)}.html`;
canonicalUrl = faker.internet.url();
name = faker.commerce.productMaterial();
description = faker.random.words(Math.floor(Math.random() * 20));
meta_title = faker.commerce.productMaterial();
meta_description = faker.random.words(Math.floor(Math.random() * 20));
breadcrumbs = [{
id: faker.datatype.uuid(),
name: faker.commerce.productMaterial(),
level: faker.datatype.number({ min: 1, max: 5 }),
url: faker.commerce.productMaterial(),
}];
name = faker.commerce.department();
description = faker.commerce.productDescription();
meta_title = faker.commerce.department();
meta_description = faker.commerce.productDescription();
breadcrumbs = this.breadcrumbFactory.createMany(faker.datatype.number({ min: 1, max: 5 }));
children_count = faker.datatype.number({ min: 1, max: 10 });
total_products = 1;
total_products = faker.datatype.number({ min: 0, max: 9999 });
product_ids = [faker.datatype.number({ min: 1, max: 100 }).toString()];

constructor(
protected breadcrumbFactory: DaffCategoryBreadcrumbFactory,
) {}
}

/**
Expand All @@ -29,8 +32,42 @@ export class MockCategory implements DaffCategory {
@Injectable({
providedIn: 'root',
})
export class DaffCategoryFactory extends DaffModelFactory<DaffCategory>{
constructor() {
super(MockCategory);
export class DaffCategoryFactory extends DaffModelFactory<DaffCategory, typeof MockCategory>{
constructor(
breadcrumbFactory: DaffCategoryBreadcrumbFactory,
) {
super(MockCategory, breadcrumbFactory);
}

/**
* Creates a category tree of specified depth, optionally using the passed product IDs.
* This is very useful for creating a category tree that closely resembles those found in the wild.
* Each child has a minimum of half of the parent's products.
*/
createTree(depth: number, productIds: DaffProduct['id'][] = [], partial: Partial<DaffCategory> = {}): DaffCategory {
if (depth > 0) {
const childrenCount = faker.datatype.number({ min: 1, max: depth * 2 });

return this.create({
children: Array(childrenCount).fill(0).map(() =>
this.createTree(
depth - 1,
randomSubset(productIds, faker.datatype.number({ min: Math.floor(productIds.length / 2), max: productIds.length })),
),
),
children_count: childrenCount,
product_ids: productIds,
total_products: productIds.length,
...partial,
});
}

return this.create({
children: [],
children_count: 0,
product_ids: productIds,
total_products: productIds.length,
...partial,
});
}
}
8 changes: 3 additions & 5 deletions libs/category/testing/src/factories/public_api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export { DaffCategoryFactory } from './category.factory';
export {
DaffCategoryPageMetadataFactory,
MockCategoryPageMetadata,
} from './category-page-metadata.factory';
export * from './category.factory';
export * from './category-breadcrumb.factory';
export * from './category-page-metadata.factory';

0 comments on commit 556e69c

Please sign in to comment.