@infrab4a/connect-angular 5.8.20 → 5.8.21-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -55,7 +55,7 @@ describe('NewCategoryStructureAdapter', () => {
|
|
|
55
55
|
|
|
56
56
|
const result = await adapter.buildProductFilterByCategory(category)
|
|
57
57
|
|
|
58
|
-
expect(categoryRepository.get).
|
|
58
|
+
expect(categoryRepository.get).toHaveBeenCalledWith({ id: '12' })
|
|
59
59
|
expect(result).toEqual({ id: { operator: Where.IN, value: ['9'] } })
|
|
60
60
|
})
|
|
61
61
|
})
|
|
@@ -18,6 +18,16 @@ describe('CategorySearchStrategy', () => {
|
|
|
18
18
|
count: 0,
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
const daysAgo = (days: number): Date => new Date(Date.now() - days * 24 * 60 * 60 * 1000)
|
|
22
|
+
|
|
23
|
+
const buildProduct = (overrides: Partial<Product> & { id: string }): Product =>
|
|
24
|
+
({
|
|
25
|
+
stock: { quantity: 10 },
|
|
26
|
+
shoppingCount: 0,
|
|
27
|
+
createdAt: daysAgo(60),
|
|
28
|
+
...overrides,
|
|
29
|
+
}) as Product
|
|
30
|
+
|
|
21
31
|
beforeEach(() => {
|
|
22
32
|
findCatalog = jasmine.createSpy('findCatalog').and.resolveTo(emptyResult)
|
|
23
33
|
buildProductFilterByCategory = jasmine
|
|
@@ -90,4 +100,115 @@ describe('CategorySearchStrategy', () => {
|
|
|
90
100
|
expect(findCatalog.calls.mostRecent().args[0].orderBy).toEqual({ createdAt: 'desc' })
|
|
91
101
|
expect(findCatalog.calls.mostRecent().args[0].limits).toEqual(limits)
|
|
92
102
|
})
|
|
103
|
+
|
|
104
|
+
it('should keep wishlist most-relevant as best-sellers without pins/Novo block', async () => {
|
|
105
|
+
const category = { id: '10', isWishlist: true, products: ['1', '2'] } as Category
|
|
106
|
+
|
|
107
|
+
await strategy.search({ category, filters: {}, limits, sort: 'most-relevant' }, shop)
|
|
108
|
+
|
|
109
|
+
expect(findCatalog).toHaveBeenCalledTimes(1)
|
|
110
|
+
expect(findCatalog.calls.mostRecent().args[0].orderBy.shoppingCount).toBe('desc')
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
describe('most-relevant in-memory order', () => {
|
|
114
|
+
const setupMostRelevant = (products: Product[], pinIds: string[]) => {
|
|
115
|
+
const category = {
|
|
116
|
+
id: '20',
|
|
117
|
+
isWishlist: false,
|
|
118
|
+
getMostRelevantByShop: () => pinIds,
|
|
119
|
+
} as unknown as Category
|
|
120
|
+
|
|
121
|
+
findCatalog.and.callFake(async (params: any) => {
|
|
122
|
+
if (params.fields?.length === 1 && params.fields[0] === 'id') {
|
|
123
|
+
return { data: products.map((p) => ({ id: p.id })), count: products.length }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { data: products, count: products.length }
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
return category
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
it('should keep in-stock pin first when most-relevant', async () => {
|
|
133
|
+
const products = [
|
|
134
|
+
buildProduct({ id: 'C', shoppingCount: 100, createdAt: daysAgo(60) }),
|
|
135
|
+
buildProduct({ id: 'P', shoppingCount: 1, createdAt: daysAgo(60) }),
|
|
136
|
+
]
|
|
137
|
+
const category = setupMostRelevant(products, ['P'])
|
|
138
|
+
|
|
139
|
+
const result = await strategy.search({ category, filters: {}, limits, sort: 'most-relevant' }, shop)
|
|
140
|
+
|
|
141
|
+
expect(result.data.map((p) => p.id)).toEqual(['P', 'C'])
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('should place in-stock Novo after pins ordered by createdAt desc', async () => {
|
|
145
|
+
const products = [
|
|
146
|
+
buildProduct({ id: 'C', shoppingCount: 100, createdAt: daysAgo(60) }),
|
|
147
|
+
buildProduct({ id: 'B', shoppingCount: 5, createdAt: daysAgo(20) }),
|
|
148
|
+
buildProduct({ id: 'A', shoppingCount: 5, createdAt: daysAgo(5) }),
|
|
149
|
+
buildProduct({ id: 'P', shoppingCount: 1, createdAt: daysAgo(60) }),
|
|
150
|
+
]
|
|
151
|
+
const category = setupMostRelevant(products, ['P'])
|
|
152
|
+
|
|
153
|
+
const result = await strategy.search({ category, filters: {}, limits, sort: 'most-relevant' }, shop)
|
|
154
|
+
|
|
155
|
+
expect(result.data.map((p) => p.id)).toEqual(['P', 'A', 'B', 'C'])
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('should not duplicate a pinned Novo', async () => {
|
|
159
|
+
const products = [
|
|
160
|
+
buildProduct({ id: 'C', shoppingCount: 50, createdAt: daysAgo(60) }),
|
|
161
|
+
buildProduct({ id: 'P', shoppingCount: 1, createdAt: daysAgo(3) }),
|
|
162
|
+
]
|
|
163
|
+
const category = setupMostRelevant(products, ['P'])
|
|
164
|
+
|
|
165
|
+
const result = await strategy.search({ category, filters: {}, limits, sort: 'most-relevant' }, shop)
|
|
166
|
+
|
|
167
|
+
expect(result.data.map((p) => p.id)).toEqual(['P', 'C'])
|
|
168
|
+
expect(result.data.filter((p) => p.id === 'P').length).toBe(1)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('should keep age 0 and 30 days in Novo block and exclude 31 days', async () => {
|
|
172
|
+
const products = [
|
|
173
|
+
buildProduct({ id: 'old', shoppingCount: 80, createdAt: daysAgo(31) }),
|
|
174
|
+
buildProduct({ id: 'edge', shoppingCount: 5, createdAt: daysAgo(30) }),
|
|
175
|
+
buildProduct({ id: 'fresh', shoppingCount: 5, createdAt: daysAgo(0) }),
|
|
176
|
+
]
|
|
177
|
+
const category = setupMostRelevant(products, [])
|
|
178
|
+
|
|
179
|
+
const result = await strategy.search({ category, filters: {}, limits, sort: 'most-relevant' }, shop)
|
|
180
|
+
|
|
181
|
+
expect(result.data.map((p) => p.id)).toEqual(['fresh', 'edge', 'old'])
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('should not treat invalid or future createdAt as Novo', async () => {
|
|
185
|
+
const products = [
|
|
186
|
+
buildProduct({ id: 'best', shoppingCount: 90, createdAt: daysAgo(60) }),
|
|
187
|
+
buildProduct({ id: 'invalid', shoppingCount: 5, createdAt: 'not-a-date' as any }),
|
|
188
|
+
buildProduct({ id: 'future', shoppingCount: 5, createdAt: new Date(Date.now() + 86400000) }),
|
|
189
|
+
]
|
|
190
|
+
const category = setupMostRelevant(products, [])
|
|
191
|
+
|
|
192
|
+
const result = await strategy.search({ category, filters: {}, limits, sort: 'most-relevant' }, shop)
|
|
193
|
+
|
|
194
|
+
expect(result.data.map((p) => p.id)).toEqual(['best', 'invalid', 'future'])
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('should not boost OOS Novo above in-stock bestsellers', async () => {
|
|
198
|
+
const products = [
|
|
199
|
+
buildProduct({ id: 'best', shoppingCount: 100, createdAt: daysAgo(60), stock: { quantity: 5 } as any }),
|
|
200
|
+
buildProduct({
|
|
201
|
+
id: 'oos-novo',
|
|
202
|
+
shoppingCount: 1,
|
|
203
|
+
createdAt: daysAgo(2),
|
|
204
|
+
stock: { quantity: 0 } as any,
|
|
205
|
+
}),
|
|
206
|
+
]
|
|
207
|
+
const category = setupMostRelevant(products, [])
|
|
208
|
+
|
|
209
|
+
const result = await strategy.search({ category, filters: {}, limits, sort: 'most-relevant' }, shop)
|
|
210
|
+
|
|
211
|
+
expect(result.data.map((p) => p.id)).toEqual(['best', 'oos-novo'])
|
|
212
|
+
})
|
|
213
|
+
})
|
|
93
214
|
})
|
|
@@ -164,9 +164,16 @@ export class CategorySearchStrategy implements CatalogSearchStrategy {
|
|
|
164
164
|
const firstProducts = totalResult.data
|
|
165
165
|
.filter((product: any) => mostRelevants.includes(product.id) && product.stock.quantity > 0)
|
|
166
166
|
.sort((a: any, b: any) => mostRelevants.indexOf(a.id) - mostRelevants.indexOf(b.id))
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
|
|
168
|
+
const nonPinnedProducts = totalResult.data.filter((product: any) => !mostRelevants.includes(product.id))
|
|
169
|
+
const novoProducts = nonPinnedProducts
|
|
170
|
+
.filter((product: any) => product.stock?.quantity > 0 && this.isCreatedWithinLastDays(product.createdAt, 30))
|
|
171
|
+
.sort((a: any, b: any) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
|
172
|
+
const restProducts = nonPinnedProducts.filter(
|
|
173
|
+
(product: any) => !(product.stock?.quantity > 0 && this.isCreatedWithinLastDays(product.createdAt, 30)),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
const lastProducts = novoProducts.concat(restProducts).concat(mostRelevantWithoutStock)
|
|
170
177
|
const categoryMostRelevants = firstProducts.concat(lastProducts)
|
|
171
178
|
const resultFinal = categoryMostRelevants.slice(limits.offset, limits.offset + limits.limit)
|
|
172
179
|
|
|
@@ -178,4 +185,23 @@ export class CategorySearchStrategy implements CatalogSearchStrategy {
|
|
|
178
185
|
distinct: totalResult.distinct,
|
|
179
186
|
}
|
|
180
187
|
}
|
|
188
|
+
|
|
189
|
+
private isCreatedWithinLastDays(createdAt: string | Date | undefined, days: number): boolean {
|
|
190
|
+
if (!createdAt || days <= 0) {
|
|
191
|
+
return false
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const createdDate = createdAt instanceof Date ? createdAt : new Date(createdAt)
|
|
195
|
+
if (Number.isNaN(createdDate.getTime())) {
|
|
196
|
+
return false
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const diffMs = Date.now() - createdDate.getTime()
|
|
200
|
+
if (diffMs < 0) {
|
|
201
|
+
return false
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const diffDays = diffMs / (1000 * 60 * 60 * 24)
|
|
205
|
+
return diffDays <= days
|
|
206
|
+
}
|
|
181
207
|
}
|
package/src/test.ts
CHANGED