Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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)}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)}`);
}
Expand Down
13 changes: 10 additions & 3 deletions src/forms-opportunities/guidance-handlers/suggestion-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*/

import { v4 as uuidv4 } from 'uuid';
import { Suggestion as SuggestionDataAccess } from '@adobe/spacecat-shared-data-access';

function getEmptyVariationList() {
return [
Expand All @@ -35,17 +36,23 @@ function getEmptyVariationList() {
export async function addSuggestions(
opportunity,
newSuggestions,
context = {},
) {
let variations = [];

if (newSuggestions) {
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;
}
Expand All @@ -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(),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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' }),
Expand Down
Loading