@@ -27,6 +27,33 @@ describe('CWV Demo Suggestions Processor Task', () => {
2727 let mockSuggestions ;
2828 let mockSuggestionDataAccess ;
2929
30+ // Helper function to create mock suggestions
31+ const createMockSuggestion = ( id , pageviews , metrics , hasIssues = false ) => ( {
32+ getId : sandbox . stub ( ) . returns ( id ) ,
33+ getData : sandbox . stub ( ) . returns ( {
34+ pageviews,
35+ metrics,
36+ ...( hasIssues && { issues : [ { type : 'lcp' , value : 'existing issue' } ] } ) ,
37+ } ) ,
38+ setData : sandbox . stub ( ) ,
39+ setUpdatedBy : sandbox . stub ( ) ,
40+ save : sandbox . stub ( ) . resolves ( ) ,
41+ } ) ;
42+
43+ // Helper function to create mock metrics
44+ const createMockMetrics = ( lcp , cls , inp , deviceType = 'desktop' ) => ( {
45+ deviceType,
46+ lcp,
47+ cls,
48+ inp,
49+ } ) ;
50+
51+ // Helper function to setup common mocks
52+ const setupCommonMocks = ( ) => {
53+ mockContext . dataAccess . Site . findById . resolves ( mockSite ) ;
54+ mockSite . getOpportunities . resolves ( [ mockOpportunity ] ) ;
55+ } ;
56+
3057 beforeEach ( async ( ) => {
3158 sandbox = sinon . createSandbox ( ) ;
3259
@@ -62,82 +89,20 @@ describe('CWV Demo Suggestions Processor Task', () => {
6289 getSuggestions : sandbox . stub ( ) ,
6390 } ;
6491
65- // Mock suggestions
92+ // Create mock suggestions using helper functions
6693 mockSuggestions = [
67- {
68- getId : sandbox . stub ( ) . returns ( 'suggestion-1' ) ,
69- getData : sandbox . stub ( ) . returns ( {
70- pageviews : 10000 ,
71- metrics : [
72- {
73- deviceType : 'desktop' ,
74- lcp : 3000 , // Above threshold
75- cls : 0.05 , // Below threshold
76- inp : 250 , // Above threshold
77- } ,
78- ] ,
79- } ) ,
80- setData : sandbox . stub ( ) ,
81- setUpdatedBy : sandbox . stub ( ) ,
82- save : sandbox . stub ( ) . resolves ( ) ,
83- } ,
84- {
85- getId : sandbox . stub ( ) . returns ( 'suggestion-2' ) ,
86- getData : sandbox . stub ( ) . returns ( {
87- pageviews : 5000 ,
88- metrics : [
89- {
90- deviceType : 'mobile' ,
91- lcp : 2000 , // Below threshold
92- cls : 0.15 , // Above threshold
93- inp : 150 , // Below threshold
94- } ,
95- ] ,
96- } ) ,
97- setData : sandbox . stub ( ) ,
98- setUpdatedBy : sandbox . stub ( ) ,
99- save : sandbox . stub ( ) . resolves ( ) ,
100- } ,
94+ createMockSuggestion ( 'suggestion-1' , 10000 , [
95+ createMockMetrics ( 3000 , 0.05 , 250 ) , // Above LCP & INP thresholds
96+ ] ) ,
97+ createMockSuggestion ( 'suggestion-2' , 5000 , [
98+ createMockMetrics ( 2000 , 0.15 , 150 , 'mobile' ) , // Above CLS threshold
99+ ] ) ,
101100 ] ;
102101
103- // Mock suggestion objects returned by findById
104- const mockSuggestion1 = {
105- getData : sandbox . stub ( ) . returns ( {
106- pageviews : 10000 ,
107- metrics : [
108- {
109- deviceType : 'desktop' ,
110- lcp : 3000 , // Above threshold
111- cls : 0.05 , // Below threshold
112- inp : 250 , // Above threshold
113- } ,
114- ] ,
115- } ) ,
116- setData : sandbox . stub ( ) ,
117- setUpdatedBy : sandbox . stub ( ) ,
118- save : sandbox . stub ( ) . resolves ( ) ,
119- } ;
120-
121- const mockSuggestion2 = {
122- getData : sandbox . stub ( ) . returns ( {
123- pageviews : 5000 ,
124- metrics : [
125- {
126- deviceType : 'mobile' ,
127- lcp : 2000 , // Below threshold
128- cls : 0.15 , // Above threshold
129- inp : 150 , // Below threshold
130- } ,
131- ] ,
132- } ) ,
133- setData : sandbox . stub ( ) ,
134- setUpdatedBy : sandbox . stub ( ) ,
135- save : sandbox . stub ( ) . resolves ( ) ,
136- } ;
137-
138102 // Setup findById to return the appropriate mock suggestion
139- mockSuggestionDataAccess . findById . withArgs ( 'suggestion-1' ) . resolves ( mockSuggestion1 ) ;
140- mockSuggestionDataAccess . findById . withArgs ( 'suggestion-2' ) . resolves ( mockSuggestion2 ) ;
103+ mockSuggestions . forEach ( ( suggestion , index ) => {
104+ mockSuggestionDataAccess . findById . withArgs ( `suggestion-${ index + 1 } ` ) . resolves ( suggestion ) ;
105+ } ) ;
141106 } ) ;
142107
143108 afterEach ( ( ) => {
@@ -167,17 +132,10 @@ describe('CWV Demo Suggestions Processor Task', () => {
167132
168133 it ( 'should skip processing when opportunity already has suggestions with issues' , async ( ) => {
169134 const suggestionsWithIssues = [
170- {
171- getId : sandbox . stub ( ) . returns ( 'suggestion-with-issues' ) ,
172- getData : sandbox . stub ( ) . returns ( {
173- pageviews : 10000 ,
174- issues : [ { type : 'lcp' , value : 'existing issue' } ] ,
175- } ) ,
176- } ,
135+ createMockSuggestion ( 'suggestion-with-issues' , 10000 , [ ] , true ) , // hasIssues = true
177136 ] ;
178137
179- mockContext . dataAccess . Site . findById . resolves ( mockSite ) ;
180- mockSite . getOpportunities . resolves ( [ mockOpportunity ] ) ;
138+ setupCommonMocks ( ) ;
181139 mockOpportunity . getSuggestions . resolves ( suggestionsWithIssues ) ;
182140
183141 const result = await runCwvDemoSuggestionsProcessor ( mockMessage , mockContext ) ;
@@ -187,8 +145,7 @@ describe('CWV Demo Suggestions Processor Task', () => {
187145 } ) ;
188146
189147 it ( 'should add generic suggestions to opportunities without issues' , async ( ) => {
190- mockContext . dataAccess . Site . findById . resolves ( mockSite ) ;
191- mockSite . getOpportunities . resolves ( [ mockOpportunity ] ) ;
148+ setupCommonMocks ( ) ;
192149 mockOpportunity . getSuggestions . resolves ( mockSuggestions ) ;
193150
194151 const result = await runCwvDemoSuggestionsProcessor ( mockMessage , mockContext ) ;
@@ -209,53 +166,21 @@ describe('CWV Demo Suggestions Processor Task', () => {
209166 it ( 'should process only first 2 suggestions with CWV issues' , async ( ) => {
210167 const manySuggestions = [
211168 ...mockSuggestions ,
212- {
213- getId : sandbox . stub ( ) . returns ( 'suggestion-3' ) ,
214- getData : sandbox . stub ( ) . returns ( {
215- pageviews : 3000 ,
216- metrics : [
217- {
218- deviceType : 'desktop' ,
219- lcp : 2800 , // Above threshold
220- cls : 0.05 , // Below threshold
221- inp : 180 , // Below threshold
222- } ,
223- ] ,
224- } ) ,
225- } ,
169+ createMockSuggestion ( 'suggestion-3' , 3000 , [
170+ createMockMetrics ( 2800 , 0.05 , 180 ) , // Above LCP threshold
171+ ] ) ,
226172 ] ;
227173
228- mockContext . dataAccess . Site . findById . resolves ( mockSite ) ;
229- mockSite . getOpportunities . resolves ( [ mockOpportunity ] ) ;
174+ setupCommonMocks ( ) ;
230175 mockOpportunity . getSuggestions . resolves ( manySuggestions ) ;
231176
232- // Mock the third suggestion for findById
233- const mockSuggestion3 = {
234- getData : sandbox . stub ( ) . returns ( {
235- pageviews : 3000 ,
236- metrics : [
237- {
238- deviceType : 'desktop' ,
239- lcp : 2800 , // Above threshold
240- cls : 0.05 , // Below threshold
241- inp : 180 , // Below threshold
242- } ,
243- ] ,
244- } ) ,
245- setData : sandbox . stub ( ) ,
246- setUpdatedBy : sandbox . stub ( ) ,
247- save : sandbox . stub ( ) . resolves ( ) ,
248- } ;
249- mockSuggestionDataAccess . findById . withArgs ( 'suggestion-3' ) . resolves ( mockSuggestion3 ) ;
250-
251177 const result = await runCwvDemoSuggestionsProcessor ( mockMessage , mockContext ) ;
252178
253179 expect ( result . message ) . to . include ( 'CWV demo suggestions processor completed' ) ;
254180 } ) ;
255181
256182 it ( 'should handle suggestion not found during update' , async ( ) => {
257- mockContext . dataAccess . Site . findById . resolves ( mockSite ) ;
258- mockSite . getOpportunities . resolves ( [ mockOpportunity ] ) ;
183+ setupCommonMocks ( ) ;
259184 mockOpportunity . getSuggestions . resolves ( mockSuggestions ) ;
260185
261186 const result = await runCwvDemoSuggestionsProcessor ( mockMessage , mockContext ) ;
@@ -265,24 +190,12 @@ describe('CWV Demo Suggestions Processor Task', () => {
265190
266191 it ( 'should handle case when no suggestions meet CWV criteria' , async ( ) => {
267192 const suggestionsWithoutCWVIssues = [
268- {
269- getId : sandbox . stub ( ) . returns ( 'no-cwv-issues' ) ,
270- getData : sandbox . stub ( ) . returns ( {
271- pageviews : 10000 ,
272- metrics : [
273- {
274- deviceType : 'desktop' ,
275- lcp : 2000 , // Below threshold
276- cls : 0.05 , // Below threshold
277- inp : 150 , // Below threshold
278- } ,
279- ] ,
280- } ) ,
281- } ,
193+ createMockSuggestion ( 'no-cwv-issues' , 10000 , [
194+ createMockMetrics ( 2000 , 0.05 , 150 ) , // All below thresholds
195+ ] ) ,
282196 ] ;
283197
284- mockContext . dataAccess . Site . findById . resolves ( mockSite ) ;
285- mockSite . getOpportunities . resolves ( [ mockOpportunity ] ) ;
198+ setupCommonMocks ( ) ;
286199 mockOpportunity . getSuggestions . resolves ( suggestionsWithoutCWVIssues ) ;
287200
288201 const result = await runCwvDemoSuggestionsProcessor ( mockMessage , mockContext ) ;
@@ -294,17 +207,10 @@ describe('CWV Demo Suggestions Processor Task', () => {
294207
295208 it ( 'should handle suggestions with missing metrics property' , async ( ) => {
296209 const suggestionsWithMissingMetrics = [
297- {
298- getId : sandbox . stub ( ) . returns ( 'missing-metrics' ) ,
299- getData : sandbox . stub ( ) . returns ( {
300- pageviews : 10000 ,
301- // No metrics property - this will trigger the || [] fallback
302- } ) ,
303- } ,
210+ createMockSuggestion ( 'missing-metrics' , 10000 , undefined ) , // No metrics property
304211 ] ;
305212
306- mockContext . dataAccess . Site . findById . resolves ( mockSite ) ;
307- mockSite . getOpportunities . resolves ( [ mockOpportunity ] ) ;
213+ setupCommonMocks ( ) ;
308214 mockOpportunity . getSuggestions . resolves ( suggestionsWithMissingMetrics ) ;
309215
310216 const result = await runCwvDemoSuggestionsProcessor ( mockMessage , mockContext ) ;
@@ -384,66 +290,36 @@ describe('CWV Demo Suggestions Processor Task', () => {
384290
385291 it ( 'should handle missing CWV reference suggestions gracefully' , async ( ) => {
386292 // This test covers the case where getRandomSuggestion returns null (lines 89-90)
387- // We'll test the getRandomSuggestion function directly with a non-existent issue type
293+ // We'll test the getRandomSuggestion function indirectly by creating a scenario
294+ // where it would be called with an issue type that doesn't exist
388295
389296 const module = await import ( '../../../src/tasks/cwv-demo-suggestions-processor/handler.js' ) ;
390297
391- // Test getRandomSuggestion function directly with non-existent issue type
392- // This should trigger the null return path (lines 89-90)
393-
394- // Since getRandomSuggestion is not exported, we need to test it indirectly
395- // by creating a scenario where it would be called with an issue type that doesn't exist
396-
397298 // Create suggestions with metrics that would trigger CWV issues
398299 const suggestionsWithCWVIssues = [
399- {
400- getId : sandbox . stub ( ) . returns ( 'suggestion-with-issues' ) ,
401- getData : sandbox . stub ( ) . returns ( {
402- pageviews : 10000 ,
403- metrics : [
404- {
405- deviceType : 'desktop' ,
406- lcp : 3000 , // Above threshold - will trigger LCP issue
407- cls : 0.05 , // Below threshold
408- inp : 150 , // Below threshold
409- } ,
410- ] ,
411- } ) ,
412- } ,
300+ createMockSuggestion ( 'suggestion-with-issues' , 10000 , [
301+ createMockMetrics ( 3000 , 0.05 , 150 ) , // Above LCP threshold
302+ ] ) ,
413303 ] ;
414304
415- // Mock the suggestion that will be found by findById
416- const mockSuggestionWithIssues = {
417- getData : sandbox . stub ( ) . returns ( {
418- pageviews : 10000 ,
419- metrics : [ {
420- deviceType : 'desktop' , lcp : 3000 , cls : 0.05 , inp : 150 ,
421- } ] ,
422- } ) ,
423- setData : sandbox . stub ( ) ,
424- setUpdatedBy : sandbox . stub ( ) ,
425- save : sandbox . stub ( ) . resolves ( ) ,
426- } ;
427-
428- mockContext . dataAccess . Site . findById . resolves ( mockSite ) ;
429- mockSite . getOpportunities . resolves ( [ mockOpportunity ] ) ;
305+ setupCommonMocks ( ) ;
430306 mockOpportunity . getSuggestions . resolves ( suggestionsWithCWVIssues ) ;
431- mockSuggestionDataAccess . findById . withArgs ( 'suggestion-with-issues' ) . resolves ( mockSuggestionWithIssues ) ;
432307
433308 // Temporarily remove all suggestions from lcp to trigger the null return path
434- const originalLcp = module . CWV_REFERENCE_SUGGESTIONS ?. lcp ;
435- if ( module . CWV_REFERENCE_SUGGESTIONS && module . CWV_REFERENCE_SUGGESTIONS . lcp ) {
436- module . CWV_REFERENCE_SUGGESTIONS . lcp = [ ] ;
309+ const originalLcp = module . cwvReferenceSuggestions ?. lcp ;
310+ if ( module . cwvReferenceSuggestions && module . cwvReferenceSuggestions . lcp ) {
311+ module . cwvReferenceSuggestions . lcp = [ ] ;
437312 }
438313
439- const result = await runCwvDemoSuggestionsProcessor ( mockMessage , mockContext ) ;
440-
441- // Restore original lcp suggestions
442- if ( module . CWV_REFERENCE_SUGGESTIONS && originalLcp ) {
443- module . CWV_REFERENCE_SUGGESTIONS . lcp = originalLcp ;
314+ try {
315+ const result = await runCwvDemoSuggestionsProcessor ( mockMessage , mockContext ) ;
316+ expect ( result . message ) . to . include ( 'CWV demo suggestions processor completed' ) ;
317+ } finally {
318+ // Restore original lcp suggestions
319+ if ( module . cwvReferenceSuggestions && originalLcp ) {
320+ module . cwvReferenceSuggestions . lcp = originalLcp ;
321+ }
444322 }
445-
446- expect ( result . message ) . to . include ( 'CWV demo suggestions processor completed' ) ;
447323 } ) ;
448324
449325 it ( 'should handle JSON file loading failure gracefully' , async ( ) => {
0 commit comments