Skip to content

Commit abd06c8

Browse files
committed
feat: adding empty suggestion for forms opportunity
1 parent 742e5d1 commit abd06c8

9 files changed

+664
-85
lines changed

eslint.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export default defineConfig([
4444
},
4545
rules: {
4646
'no-unused-expressions': 'off',
47-
'import/no-unresolved': ['error', { ignore: ['@octokit/rest', 'is-language-code'] }],
47+
'import/no-unresolved': ['error', { ignore: ['@octokit/rest', 'is-language-code', 'uuid'] }],
4848
},
4949
},
5050
{

package-lock.json

Lines changed: 297 additions & 80 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
"syllable": "5.0.1",
117117
"text-readability": "1.1.1",
118118
"urijs": "1.19.11",
119+
"uuid": "^13.0.0",
119120
"zod": "4.1.12"
120121
},
121122
"devDependencies": {

src/forms-opportunities/guidance-handlers/guidance-high-form-views-low-conversions.js

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,77 @@
1111
*/
1212

1313
import { ok } from '@adobe/spacecat-shared-http-utils';
14+
import { v4 as uuidv4 } from 'uuid';
1415
import { FORM_OPPORTUNITY_TYPES, ORIGINS } from '../constants.js';
1516

17+
/**
18+
* Fetches existing suggestions and merges them with new suggestions
19+
* @param opportunity
20+
* @param newSuggestions
21+
* @returns {Promise<void>}
22+
*/
23+
async function addSuggestions(
24+
opportunity,
25+
newSuggestions,
26+
) {
27+
const existingSuggestions = await opportunity.getSuggestions();
28+
29+
if (
30+
(existingSuggestions && existingSuggestions.length > 0)
31+
|| (newSuggestions && newSuggestions.length > 0)
32+
) {
33+
// merge existing and new suggestions and add to opportunity.
34+
// To be done once M starts generating suggestions for this guidance
35+
} else {
36+
const emptySuggestionList = [
37+
{
38+
id: uuidv4(),
39+
opportunityId: opportunity.opportunityId,
40+
type: 'CONTENT_UPDATE',
41+
rank: 1,
42+
status: 'NEW',
43+
data: {
44+
variations: [
45+
{
46+
name: 'Control',
47+
changes: [
48+
{
49+
type: 'text',
50+
element: null,
51+
text: 'Control',
52+
},
53+
],
54+
variationEditPageUrl: null,
55+
id: uuidv4(),
56+
variationPageUrl: '',
57+
explanation: null,
58+
projectedImpact: null,
59+
previewImage: '',
60+
},
61+
],
62+
},
63+
kpiDeltas: {
64+
estimatedKPILift: 0,
65+
},
66+
createdAt: new Date().toISOString(),
67+
updatedAt: new Date().toISOString(),
68+
updatedBy: 'system',
69+
},
70+
];
71+
await opportunity.addSuggestions(emptySuggestionList);
72+
}
73+
}
74+
1675
export default async function handler(message, context) {
1776
const { log, dataAccess } = context;
1877
const { Opportunity } = dataAccess;
1978
const { auditId, siteId, data } = message;
20-
const { url, guidance, form_source: formsource } = data;
79+
const {
80+
url,
81+
guidance,
82+
form_source: formsource,
83+
suggestions,
84+
} = data;
2185
log.info(`[Form Opportunity] [Site Id: ${siteId}] message received in high-form-views-low-conversions guidance handler: ${JSON.stringify(message, null, 2)}`);
2286

2387
const existingOpportunities = await Opportunity.allBySiteId(siteId);
@@ -34,6 +98,7 @@ export default async function handler(message, context) {
3498
const wrappedGuidance = { recommendations: guidance };
3599
opportunity.setGuidance(wrappedGuidance);
36100
opportunity.setUpdatedBy('system');
101+
await addSuggestions(opportunity, suggestions);
37102
await opportunity.save();
38103
log.debug(`[Form Opportunity] [Site Id: ${siteId}] high-form-views-low-conversions guidance updated oppty : ${JSON.stringify(opportunity, null, 2)}`);
39104
}

src/forms-opportunities/guidance-handlers/guidance-high-page-views-low-form-nav.js

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,77 @@
1111
*/
1212

1313
import { ok } from '@adobe/spacecat-shared-http-utils';
14+
import { v4 as uuidv4 } from 'uuid';
1415
import { FORM_OPPORTUNITY_TYPES, ORIGINS } from '../constants.js';
1516

17+
/**
18+
* Fetches existing suggestions and merges them with new suggestions
19+
* @param opportunity
20+
* @param newSuggestions
21+
* @returns {Promise<void>}
22+
*/
23+
async function addSuggestions(
24+
opportunity,
25+
newSuggestions,
26+
) {
27+
const existingSuggestions = await opportunity.getSuggestions();
28+
29+
if (
30+
(existingSuggestions && existingSuggestions.length > 0)
31+
|| (newSuggestions && newSuggestions.length > 0)
32+
) {
33+
// merge existing and new suggestions and add to opportunity.
34+
// To be done once M starts generating suggestions for this guidance
35+
} else {
36+
const emptySuggestionList = [
37+
{
38+
id: uuidv4(),
39+
opportunityId: opportunity.opportunityId,
40+
type: 'CONTENT_UPDATE',
41+
rank: 1,
42+
status: 'NEW',
43+
data: {
44+
variations: [
45+
{
46+
name: 'Control',
47+
changes: [
48+
{
49+
type: 'text',
50+
element: null,
51+
text: 'Control',
52+
},
53+
],
54+
variationEditPageUrl: null,
55+
id: uuidv4(),
56+
variationPageUrl: '',
57+
explanation: null,
58+
projectedImpact: null,
59+
previewImage: '',
60+
},
61+
],
62+
},
63+
kpiDeltas: {
64+
estimatedKPILift: 0,
65+
},
66+
createdAt: new Date().toISOString(),
67+
updatedAt: new Date().toISOString(),
68+
updatedBy: 'system',
69+
},
70+
];
71+
await opportunity.addSuggestions(emptySuggestionList);
72+
}
73+
}
74+
1675
export default async function handler(message, context) {
1776
const { log, dataAccess } = context;
1877
const { Opportunity } = dataAccess;
1978
const { auditId, siteId, data } = message;
20-
const { url, guidance, form_source: formsource } = data;
79+
const {
80+
url,
81+
guidance,
82+
form_source: formsource,
83+
suggestions,
84+
} = data;
2185
log.info(`[Form Opportunity] [Site Id: ${siteId}] message received in high-page-views-low-form-nav guidance handler: ${JSON.stringify(message, null, 2)}`);
2286

2387
const existingOpportunities = await Opportunity.allBySiteId(siteId);
@@ -34,6 +98,7 @@ export default async function handler(message, context) {
3498
const wrappedGuidance = { recommendations: guidance };
3599
opportunity.setGuidance(wrappedGuidance);
36100
opportunity.setUpdatedBy('system');
101+
await addSuggestions(opportunity, suggestions);
37102
await opportunity.save();
38103
log.debug(`[Form Opportunity] [Site Id: ${siteId}] high-page-views-low-form-nav guidance updated oppty: ${JSON.stringify(opportunity, null, 2)}`);
39104
}

src/forms-opportunities/guidance-handlers/guidance-high-page-views-low-form-views.js

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,76 @@
1111
*/
1212

1313
import { ok } from '@adobe/spacecat-shared-http-utils';
14+
import { v4 as uuidv4 } from 'uuid';
1415
import { FORM_OPPORTUNITY_TYPES, ORIGINS } from '../constants.js';
1516

17+
/**
18+
* Fetches existing suggestions and merges them with new suggestions
19+
* @param opportunity
20+
* @param newSuggestions
21+
* @returns {Promise<void>}
22+
*/
23+
async function addSuggestions(
24+
opportunity,
25+
newSuggestions,
26+
) {
27+
const existingSuggestions = await opportunity.getSuggestions();
28+
29+
if (
30+
(existingSuggestions && existingSuggestions.length > 0)
31+
|| (newSuggestions && newSuggestions.length > 0)
32+
) {
33+
// merge existing and new suggestions and add to opportunity.
34+
// To be done once M starts generating suggestions for this guidance
35+
} else {
36+
const emptySuggestionList = [
37+
{
38+
id: uuidv4(),
39+
opportunityId: opportunity.opportunityId,
40+
type: 'CONTENT_UPDATE',
41+
rank: 1,
42+
status: 'NEW',
43+
data: {
44+
variations: [
45+
{
46+
name: 'Control',
47+
changes: [
48+
{
49+
type: 'text',
50+
element: null,
51+
text: 'Control',
52+
},
53+
],
54+
variationEditPageUrl: null,
55+
id: uuidv4(),
56+
variationPageUrl: '',
57+
explanation: null,
58+
projectedImpact: null,
59+
previewImage: '',
60+
},
61+
],
62+
},
63+
kpiDeltas: {
64+
estimatedKPILift: 0,
65+
},
66+
createdAt: new Date().toISOString(),
67+
updatedAt: new Date().toISOString(),
68+
updatedBy: 'system',
69+
},
70+
];
71+
await opportunity.addSuggestions(emptySuggestionList);
72+
}
73+
}
74+
1675
export default async function handler(message, context) {
1776
const { log, dataAccess } = context;
1877
const { Opportunity } = dataAccess;
1978
const { auditId, siteId, data } = message;
20-
const { url, form_source: formsource, guidance } = data;
79+
const {
80+
url,
81+
form_source: formsource,
82+
guidance, suggestions,
83+
} = data;
2184
log.info(`[Form Opportunity] [Site Id: ${siteId}] message received in high-page-views-low-form-views guidance handler: ${JSON.stringify(message, null, 2)}`);
2285

2386
const existingOpportunities = await Opportunity.allBySiteId(siteId);
@@ -34,9 +97,9 @@ export default async function handler(message, context) {
3497
const wrappedGuidance = { recommendations: guidance };
3598
opportunity.setGuidance(wrappedGuidance);
3699
opportunity.setUpdatedBy('system');
100+
await addSuggestions(opportunity, suggestions);
37101
await opportunity.save();
38102
log.debug(`[Form Opportunity] [Site Id: ${siteId}] high-page-views-low-form-views guidance updated oppty: ${JSON.stringify(opportunity, null, 2)}`);
39103
}
40-
41104
return ok();
42105
}

test/audits/forms/guidance-handlers/guidance-high-form-views-low-conversions.test.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ describe('Guidance High Form Views Low Conversions Handler', () => {
7373
save: sinon.stub().resolvesThis(),
7474
getId: sinon.stub().resolves('testId'),
7575
getSuggestions: sinon.stub().resolves([]),
76+
addSuggestions: sinon.stub(),
7677
setUpdatedBy: sinon.stub(),
7778
};
7879
dataAccessStub.Opportunity.allBySiteId.resolves([existingOpportunity]);
@@ -113,4 +114,58 @@ describe('Guidance High Form Views Low Conversions Handler', () => {
113114
expect(error.message).to.deep.equal('fetch error');
114115
}
115116
});
117+
118+
it('should create empty suggestion object if none found', async () => {
119+
const existingOpportunity = {
120+
getData: sinon.stub().returns({ form: 'https://example.com', formsource: '.form' }),
121+
getType: sinon.stub().returns(FORM_OPPORTUNITY_TYPES.LOW_CONVERSION),
122+
setAuditId: sinon.stub(),
123+
setGuidance: sinon.stub(),
124+
addSuggestions: sinon.stub(),
125+
save: sinon.stub().resolvesThis(),
126+
getId: sinon.stub().resolves('testId'),
127+
getSuggestions: sinon.stub().resolves([]),
128+
setUpdatedBy: sinon.stub(),
129+
};
130+
dataAccessStub.Opportunity.allBySiteId.resolves([existingOpportunity]);
131+
132+
const messageWithoutSuggestions = {
133+
auditId: 'audit-id',
134+
siteId: 'site-id',
135+
data: {
136+
url: 'https://example.com',
137+
form_source: '.form',
138+
guidance: 'Some guidance'
139+
},
140+
};
141+
await handler(messageWithoutSuggestions, context);
142+
expect(existingOpportunity.addSuggestions).to.be.calledOnce;
143+
});
144+
145+
it('should not create empty suggestion if any suggestion found', async () => {
146+
const existingOpportunity = {
147+
getData: sinon.stub().returns({ form: 'https://example.com', formsource: '.form' }),
148+
getType: sinon.stub().returns(FORM_OPPORTUNITY_TYPES.LOW_CONVERSION),
149+
setAuditId: sinon.stub(),
150+
setGuidance: sinon.stub(),
151+
addSuggestions: sinon.stub(),
152+
save: sinon.stub().resolvesThis(),
153+
getId: sinon.stub().resolves('testId'),
154+
getSuggestions: sinon.stub().resolves(["existing suggestion"]),
155+
setUpdatedBy: sinon.stub(),
156+
};
157+
dataAccessStub.Opportunity.allBySiteId.resolves([existingOpportunity]);
158+
159+
const messageWithoutSuggestions = {
160+
auditId: 'audit-id',
161+
siteId: 'site-id',
162+
data: {
163+
url: 'https://example.com',
164+
form_source: '.form',
165+
guidance: 'Some guidance'
166+
},
167+
};
168+
await handler(messageWithoutSuggestions, context);
169+
expect(existingOpportunity.addSuggestions).to.be.not.called;
170+
});
116171
});

0 commit comments

Comments
 (0)