diff --git a/src/forms-opportunities/guidance-handlers/guidance-high-form-views-low-conversions.js b/src/forms-opportunities/guidance-handlers/guidance-high-form-views-low-conversions.js index 45851e112a..8e3226dc07 100644 --- a/src/forms-opportunities/guidance-handlers/guidance-high-form-views-low-conversions.js +++ b/src/forms-opportunities/guidance-handlers/guidance-high-form-views-low-conversions.js @@ -40,7 +40,7 @@ export default async function handler(message, context) { const wrappedGuidance = { recommendations: guidance }; opportunity.setGuidance(wrappedGuidance); opportunity.setUpdatedBy('system'); - await addSuggestions(opportunity, suggestions); + await addSuggestions(opportunity, suggestions, context); await opportunity.save(); log.debug(`[Form Opportunity] [Site Id: ${siteId}] high-form-views-low-conversions guidance updated oppty : ${JSON.stringify(opportunity, null, 2)}`); } diff --git a/src/forms-opportunities/guidance-handlers/guidance-high-page-views-low-form-nav.js b/src/forms-opportunities/guidance-handlers/guidance-high-page-views-low-form-nav.js index 0b34506463..defbca81ab 100644 --- a/src/forms-opportunities/guidance-handlers/guidance-high-page-views-low-form-nav.js +++ b/src/forms-opportunities/guidance-handlers/guidance-high-page-views-low-form-nav.js @@ -40,7 +40,7 @@ export default async function handler(message, context) { const wrappedGuidance = { recommendations: guidance }; opportunity.setGuidance(wrappedGuidance); opportunity.setUpdatedBy('system'); - await addSuggestions(opportunity, suggestions); + await addSuggestions(opportunity, suggestions, context); await opportunity.save(); log.debug(`[Form Opportunity] [Site Id: ${siteId}] high-page-views-low-form-nav guidance updated oppty: ${JSON.stringify(opportunity, null, 2)}`); } diff --git a/src/forms-opportunities/guidance-handlers/guidance-high-page-views-low-form-views.js b/src/forms-opportunities/guidance-handlers/guidance-high-page-views-low-form-views.js index 78f52a047f..78073c0fe8 100644 --- a/src/forms-opportunities/guidance-handlers/guidance-high-page-views-low-form-views.js +++ b/src/forms-opportunities/guidance-handlers/guidance-high-page-views-low-form-views.js @@ -39,7 +39,7 @@ export default async function handler(message, context) { const wrappedGuidance = { recommendations: guidance }; opportunity.setGuidance(wrappedGuidance); opportunity.setUpdatedBy('system'); - await addSuggestions(opportunity, suggestions); + await addSuggestions(opportunity, suggestions, context); await opportunity.save(); log.debug(`[Form Opportunity] [Site Id: ${siteId}] high-page-views-low-form-views guidance updated oppty: ${JSON.stringify(opportunity, null, 2)}`); } diff --git a/src/forms-opportunities/guidance-handlers/suggestion-utils.js b/src/forms-opportunities/guidance-handlers/suggestion-utils.js index 6c1ca1212e..0c528b15ea 100644 --- a/src/forms-opportunities/guidance-handlers/suggestion-utils.js +++ b/src/forms-opportunities/guidance-handlers/suggestion-utils.js @@ -11,6 +11,7 @@ */ import { v4 as uuidv4 } from 'uuid'; +import { Suggestion as SuggestionDataAccess } from '@adobe/spacecat-shared-data-access'; function getEmptyVariationList() { return [ @@ -35,6 +36,7 @@ function getEmptyVariationList() { export async function addSuggestions( opportunity, newSuggestions, + context = {}, ) { let variations = []; @@ -42,10 +44,15 @@ export async function addSuggestions( variations = [...newSuggestions]; } + const requiresValidation = Boolean(context?.site?.requiresValidation); + const suggestionStatus = requiresValidation + ? SuggestionDataAccess.STATUSES.PENDING_VALIDATION + : SuggestionDataAccess.STATUSES.NEW; + const existingSuggestions = await opportunity.getSuggestions(); - if (existingSuggestions && existingSuggestions.length > 0) { - if (existingSuggestions[0].data && existingSuggestions[0].data.variations) { + if (existingSuggestions?.length > 0) { + if (existingSuggestions[0]?.data?.variations) { // replacing the entire variations with the new ones existingSuggestions[0].data.variations = variations; } @@ -56,7 +63,7 @@ export async function addSuggestions( opportunityId: opportunity.opportunityId, type: 'CONTENT_UPDATE', rank: 1, - status: 'PENDING_VALIDATION', + status: suggestionStatus, data: { variations: variations.length > 0 ? variations : getEmptyVariationList(), }, diff --git a/test/audits/forms/guidance-handlers/guidance-high-form-views-low-conversions.test.js b/test/audits/forms/guidance-handlers/guidance-high-form-views-low-conversions.test.js index 96204d16de..9a6bee596a 100644 --- a/test/audits/forms/guidance-handlers/guidance-high-form-views-low-conversions.test.js +++ b/test/audits/forms/guidance-handlers/guidance-high-form-views-low-conversions.test.js @@ -16,6 +16,7 @@ import { expect, use } from 'chai'; import sinon from 'sinon'; import { ok } from '@adobe/spacecat-shared-http-utils'; import sinonChai from 'sinon-chai'; +import { Suggestion as SuggestionDataAccess } from '@adobe/spacecat-shared-data-access'; import { FORM_OPPORTUNITY_TYPES } from '../../../../src/forms-opportunities/constants.js'; import handler from '../../../../src/forms-opportunities/guidance-handlers/guidance-high-form-views-low-conversions.js'; @@ -142,6 +143,105 @@ describe('Guidance High Form Views Low Conversions Handler', () => { expect(existingOpportunity.addSuggestions).to.be.calledOnce; }); + it('should create suggestion with NEW status when context.site.requiresValidation is false', async () => { + const existingOpportunity = { + getData: sinon.stub().returns({ form: 'https://example.com', formsource: '.form' }), + getType: sinon.stub().returns(FORM_OPPORTUNITY_TYPES.LOW_CONVERSION), + setAuditId: sinon.stub(), + setGuidance: sinon.stub(), + addSuggestions: sinon.stub(), + save: sinon.stub().resolvesThis(), + getId: sinon.stub().resolves('testId'), + getSuggestions: sinon.stub().resolves([]), + setUpdatedBy: sinon.stub(), + opportunityId: 'opp-123', + }; + dataAccessStub.Opportunity.allBySiteId.resolves([existingOpportunity]); + context.site = { requiresValidation: false }; + + const messageWithoutSuggestions = { + auditId: 'audit-id', + siteId: 'site-id', + data: { + url: 'https://example.com', + form_source: '.form', + guidance: 'Some guidance' + }, + }; + await handler(messageWithoutSuggestions, context); + + const addSuggestionsCall = existingOpportunity.addSuggestions.getCall(0); + expect(addSuggestionsCall).to.exist; + const suggestionList = addSuggestionsCall.args[0]; + expect(suggestionList[0].status).to.equal(SuggestionDataAccess.STATUSES.NEW); + }); + + it('should create suggestion with PENDING_VALIDATION status when context.site.requiresValidation is true', async () => { + const existingOpportunity = { + getData: sinon.stub().returns({ form: 'https://example.com', formsource: '.form' }), + getType: sinon.stub().returns(FORM_OPPORTUNITY_TYPES.LOW_CONVERSION), + setAuditId: sinon.stub(), + setGuidance: sinon.stub(), + addSuggestions: sinon.stub(), + save: sinon.stub().resolvesThis(), + getId: sinon.stub().resolves('testId'), + getSuggestions: sinon.stub().resolves([]), + opportunityId: 'opp-123', + setUpdatedBy: sinon.stub(), + }; + dataAccessStub.Opportunity.allBySiteId.resolves([existingOpportunity]); + context.site = { requiresValidation: true }; + + const messageWithoutSuggestions = { + auditId: 'audit-id', + siteId: 'site-id', + data: { + url: 'https://example.com', + form_source: '.form', + guidance: 'Some guidance' + }, + }; + await handler(messageWithoutSuggestions, context); + + const addSuggestionsCall = existingOpportunity.addSuggestions.getCall(0); + expect(addSuggestionsCall).to.exist; + const suggestionList = addSuggestionsCall.args[0]; + expect(suggestionList[0].status).to.equal(SuggestionDataAccess.STATUSES.PENDING_VALIDATION); + }); + + it('should create suggestion with NEW status when context.site.requiresValidation is undefined (backwards compat)', async () => { + const existingOpportunity = { + getData: sinon.stub().returns({ form: 'https://example.com', formsource: '.form' }), + getType: sinon.stub().returns(FORM_OPPORTUNITY_TYPES.LOW_CONVERSION), + setAuditId: sinon.stub(), + setGuidance: sinon.stub(), + addSuggestions: sinon.stub(), + save: sinon.stub().resolvesThis(), + getId: sinon.stub().resolves('testId'), + getSuggestions: sinon.stub().resolves([]), + setUpdatedBy: sinon.stub(), + opportunityId: 'opp-123', + }; + dataAccessStub.Opportunity.allBySiteId.resolves([existingOpportunity]); + // context.site not set - simulates legacy callers + + const messageWithoutSuggestions = { + auditId: 'audit-id', + siteId: 'site-id', + data: { + url: 'https://example.com', + form_source: '.form', + guidance: 'Some guidance' + }, + }; + await handler(messageWithoutSuggestions, context); + + const addSuggestionsCall = existingOpportunity.addSuggestions.getCall(0); + expect(addSuggestionsCall).to.exist; + const suggestionList = addSuggestionsCall.args[0]; + expect(suggestionList[0].status).to.equal(SuggestionDataAccess.STATUSES.NEW); + }); + it('should not create empty suggestion if any suggestion found', async () => { const existingOpportunity = { getData: sinon.stub().returns({ form: 'https://example.com', formsource: '.form' }),