PluginProbe
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management / 1.1.0
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management v1.1.0
1.6.1 1.6.0 1.5.1 1.5.0 1.4.0 1.3.0 trunk 0.0.1 1.0.0 1.1.0 1.1.1 1.1.2 1.2.0
← All changes | specs/utils/test-data.js +308 -4 1.0.0 → 1.1.0 View file →
@@ -10,8 +10,97 @@
10 10 const SD_API = '/suredonation/v1';
11 11 const CAMPAIGN_CPT = 'suredonation_cmpgn';
12 12
13 13 /**
14 + * Unwrap a settings REST response.
15 + *
16 + * Endpoints return either `{ success, settings: {...} }` or the settings object
17 + * directly; this centralizes that shape assumption so it lives in one place.
18 + *
19 + * @param {Object} response REST response.
20 + * @return {Object} The settings object.
21 + */
22 +function unwrapSettings( response ) {
23 + return ( response && response.settings ) || response || {};
24 +}
25 +
26 +let uniqueEmailCounter = 0;
27 +
28 +/**
29 + * Generate a collision-free test email.
30 + *
31 + * `Date.now()` alone can repeat within the same millisecond across fast
32 + * sequential submits, so a monotonic counter is appended.
33 + *
34 + * @param {string} prefix Email local-part prefix.
35 + * @return {string} Unique `@test.local` email address.
36 + */
37 +function uniqueEmail( prefix = 'e2e' ) {
38 + uniqueEmailCounter += 1;
39 + return `${ prefix }-${ Date.now() }-${ uniqueEmailCounter }@test.local`;
40 +}
41 +
42 +/**
43 + * Get current spam-protection (honeypot) settings via REST API.
44 + *
45 + * @param {Object} requestUtils Playwright requestUtils fixture.
46 + * @return {Promise<Object>} API response: { success, settings: { honeypot } }.
47 + */
48 +async function getSpamProtectionSettings( requestUtils ) {
49 + return requestUtils.rest( {
50 + method: 'GET',
51 + path: `${ SD_API }/settings/spam-protection`,
52 + } );
53 +}
54 +
55 +/**
56 + * Toggle the honeypot setting via REST API.
57 + *
58 + * @param {Object} requestUtils Playwright requestUtils fixture.
59 + * @param {boolean} enabled Whether the honeypot should be enabled.
60 + * @return {Promise<Object>} API response.
61 + */
62 +async function setHoneypot( requestUtils, enabled ) {
63 + try {
64 + return await requestUtils.rest( {
65 + method: 'POST',
66 + path: `${ SD_API }/settings/spam-protection`,
67 + data: { honeypot: enabled },
68 + } );
69 + } catch ( e ) {
70 + // Defensive fallback (mirrors enableOfflineDonations): if the option
71 + // write reports unchanged and the API surfaces an error, verify via GET.
72 + const settings = unwrapSettings(
73 + await getSpamProtectionSettings( requestUtils )
74 + );
75 + if ( settings.honeypot === enabled ) {
76 + return { success: true, settings };
77 + }
78 + throw e;
79 + }
80 +}
81 +
82 +/**
83 + * Enable the honeypot spam protection via REST API.
84 + *
85 + * @param {Object} requestUtils Playwright requestUtils fixture.
86 + * @return {Promise<Object>} API response.
87 + */
88 +async function enableHoneypot( requestUtils ) {
89 + return setHoneypot( requestUtils, true );
90 +}
91 +
92 +/**
93 + * Disable the honeypot spam protection via REST API.
94 + *
95 + * @param {Object} requestUtils Playwright requestUtils fixture.
96 + * @return {Promise<Object>} API response.
97 + */
98 +async function disableHoneypot( requestUtils ) {
99 + return setHoneypot( requestUtils, false );
100 +}
101 +
102 +/**
14 103 * Create a test campaign via the WP REST API.
15 104 *
16 105 * @param {Object} requestUtils Playwright requestUtils fixture.
17 106 * @return {Promise<number>} Campaign post ID.
@@ -414,9 +503,9 @@
414 503 // Inject block_id (and formId for payment block) into ALL suredonation/* blocks.
415 504 // The Gutenberg editor normally sets block_id on mount via useEffect.
416 505 // Server-side validation skips blocks without block_id, so we must inject them.
417 506 updatedContent = updatedContent.replace(
418 - /<!-- wp:sd\/(\w[\w-]*) \{(?!"block_id")/g,
507 + /<!-- wp:suredonation\/(\w[\w-]*) \{(?!"block_id")/g,
419 508 ( match, blockName ) => {
420 509 const id = Math.random().toString( 36 ).substring( 2, 9 );
421 510 if ( blockName === 'payment' ) {
422 511 return `<!-- wp:suredonation/${ blockName } {"block_id":"${ id }","formId":${ formId },`;
@@ -566,8 +655,47 @@
566 655 }
567 656 }
568 657
569 658 /**
659 + * Get the global payment settings via the Stripe settings REST API.
660 + *
661 + * @param {Object} requestUtils Playwright requestUtils fixture.
662 + * @return {Promise<Object>} Payment settings object (currency, payment_mode, …).
663 + */
664 +async function getPaymentSettings( requestUtils ) {
665 + const response = await requestUtils.rest( {
666 + method: 'GET',
667 + path: `${ SD_API }/payments/stripe/settings`,
668 + } );
669 + return response.settings || response;
670 +}
671 +
672 +/**
673 + * Set the global payment mode (test|live) via the Stripe settings REST API.
674 + *
675 + * @param {Object} requestUtils Playwright requestUtils fixture.
676 + * @param {string} mode 'test' or 'live'.
677 + * @return {Promise<Object>} API response.
678 + */
679 +async function setPaymentMode( requestUtils, mode ) {
680 + try {
681 + return await requestUtils.rest( {
682 + method: 'POST',
683 + path: `${ SD_API }/payments/stripe/settings`,
684 + data: { payment_mode: mode },
685 + } );
686 + } catch ( e ) {
687 + // WordPress update_option returns false when the value is unchanged,
688 + // surfacing as an API error. Verify via GET instead.
689 + const current = await getPaymentSettings( requestUtils );
690 + if ( current.payment_mode === mode ) {
691 + return { success: true, settings: current };
692 + }
693 + throw e;
694 + }
695 +}
696 +
697 +/**
570 698 * Get fee recovery settings via the Stripe settings REST API.
571 699 *
572 700 * @param {Object} requestUtils Playwright requestUtils fixture.
573 701 * @return {Promise<Object>} Fee recovery settings object.
@@ -668,9 +796,9 @@
668 796 }
669 797
670 798 // 5. Inject block_id into all suredonation/* blocks (same pattern as createOfflineFormPage).
671 799 updatedContent = updatedContent.replace(
672 - /<!-- wp:sd\/(\w[\w-]*) \{(?!"block_id")/g,
800 + /<!-- wp:suredonation\/(\w[\w-]*) \{(?!"block_id")/g,
673 801 ( match, blockName ) => {
674 802 const id = Math.random().toString( 36 ).substring( 2, 9 );
675 803 if ( blockName === 'payment' ) {
676 804 return `<!-- wp:suredonation/${ blockName } {"block_id":"${ id }","formId":${ formId },`;
@@ -806,9 +934,9 @@
806 934 * @return {string} Updated content with block_id injected.
807 935 */
808 936 function injectBlockIds( content, formId ) {
809 937 return content.replace(
810 - /<!-- wp:sd\/(\w[\w-]*) \{(?!"block_id")/g,
938 + /<!-- wp:suredonation\/(\w[\w-]*) \{(?!"block_id")/g,
811 939 ( match, blockName ) => {
812 940 const id = Math.random().toString( 36 ).substring( 2, 9 );
813 941 if ( blockName === 'payment' ) {
814 942 return `<!-- wp:suredonation/${ blockName } {"block_id":"${ id }","formId":${ formId },`;
@@ -992,9 +1120,175 @@
992 1120 }
993 1121 }
994 1122 }
995 1123
1124 +// ─── Form Validation Test Data ──────────────────────────────────
1125 +
996 1126 /**
1127 + * Get the form-validation default messages via REST API.
1128 + *
1129 + * @param {Object} requestUtils Playwright requestUtils fixture.
1130 + * @return {Promise<Object>} API response: { success, settings: { ...messages } }.
1131 + */
1132 +async function getValidationSettings( requestUtils ) {
1133 + return requestUtils.rest( {
1134 + method: 'GET',
1135 + path: `${ SD_API }/settings/validation`,
1136 + } );
1137 +}
1138 +
1139 +/**
1140 + * Update the form-validation default messages via REST API.
1141 + *
1142 + * @param {Object} requestUtils Playwright requestUtils fixture.
1143 + * @param {Object} messages Map of message key => message.
1144 + * @return {Promise<Object>} API response.
1145 + */
1146 +async function updateValidationSettings( requestUtils, messages ) {
1147 + try {
1148 + return await requestUtils.rest( {
1149 + method: 'POST',
1150 + path: `${ SD_API }/settings/validation`,
1151 + data: messages,
1152 + } );
1153 + } catch ( e ) {
1154 + // WordPress update_option returns false when the value is unchanged,
1155 + // which can surface as an API error. Verify via GET instead.
1156 + const current = await getValidationSettings( requestUtils );
1157 + const settings = current.settings || current;
1158 + const matches = Object.keys( messages ).every(
1159 + ( key ) => settings[ key ] === messages[ key ]
1160 + );
1161 + if ( matches ) {
1162 + return { success: true, settings };
1163 + }
1164 + throw e;
1165 + }
1166 +}
1167 +
1168 +/**
1169 + * Create a donation form page for field-validation tests.
1170 + *
1171 + * Builds on the default form (name, email, donation-amount, payment,
1172 + * donate-button) and optionally injects a required number field (slug
1173 + * "quantity") with min/max bounds and/or a per-field custom Error Message on
1174 + * the name input. block_id attributes are injected so server-side validation
1175 + * runs. Rendered via the [suredonation_form] shortcode.
1176 + *
1177 + * @param {Object} requestUtils Playwright requestUtils fixture.
1178 + * @param {Object} options Options.
1179 + * @param {string} options.inputErrorMsg Custom Error Message for the name field.
1180 + * @param {number} options.minValue Number field minimum (default 5).
1181 + * @param {number} options.maxValue Number field maximum (default 50).
1182 + * @param {boolean} options.numberField Set false to omit the number field.
1183 + * @return {Promise<Object>} { pageUrl, campaignId, formId, pageId }.
1184 + */
1185 +async function createValidationFormPage( requestUtils, options = {} ) {
1186 + // Standalone so the form renders without a campaign dependency.
1187 + const { campaignId, formId } = await prepareFormAndCampaign( requestUtils, {
1188 + standalone: true,
1189 + } );
1190 +
1191 + const form = await requestUtils.rest( {
1192 + method: 'GET',
1193 + path: `/wp/v2/${ FORM_CPT }/${ formId }?context=edit`,
1194 + } );
1195 +
1196 + let content = form.content?.raw || '';
1197 +
1198 + // Inject a block_id into every suredonation/* field block that lacks one.
1199 + // The default template only sets slugs (the editor adds block_id on mount),
1200 + // and server-side validation skips blocks without a block_id — so REST-built
1201 + // forms need them injected for the stored block config to include the fields.
1202 + content = content.replace(
1203 + /<!-- wp:suredonation\/(\w[\w-]*) \{(?!"block_id")/g,
1204 + ( match, blockName ) => {
1205 + const id = Math.random().toString( 36 ).substring( 2, 9 );
1206 + if ( blockName === 'payment' ) {
1207 + return `<!-- wp:suredonation/${ blockName } {"block_id":"${ id }","formId":${ formId },`;
1208 + }
1209 + return `<!-- wp:suredonation/${ blockName } {"block_id":"${ id }",`;
1210 + }
1211 + );
1212 +
1213 + // Set a per-field custom Error Message on the name (input) block. Default
1214 + // forms omit the empty errorMsg attribute, so inject it into the opening tag.
1215 + if ( options.inputErrorMsg ) {
1216 + content = content.replace(
1217 + /<!-- wp:suredonation\/input \{/,
1218 + `<!-- wp:suredonation/input {"errorMsg":${ JSON.stringify(
1219 + options.inputErrorMsg
1220 + ) },`
1221 + );
1222 + }
1223 +
1224 + // Set a per-field custom invalid-email message on the email block. It ships
1225 + // with a non-empty default, so replace the existing value if present.
1226 + if ( options.emailInvalidMsg ) {
1227 + if ( /"invalidEmailMsg"\s*:/.test( content ) ) {
1228 + content = content.replace(
1229 + /"invalidEmailMsg"\s*:\s*"(?:[^"\\]|\\.)*"/,
1230 + `"invalidEmailMsg":${ JSON.stringify( options.emailInvalidMsg ) }`
1231 + );
1232 + } else {
1233 + content = content.replace(
1234 + /<!-- wp:suredonation\/email \{/,
1235 + `<!-- wp:suredonation/email {"invalidEmailMsg":${ JSON.stringify(
1236 + options.emailInvalidMsg
1237 + ) },`
1238 + );
1239 + }
1240 + }
1241 +
1242 + // Inject a required number field with explicit slug (so update_field_slugs
1243 + // leaves it alone) and min/max bounds, before the donate button.
1244 + if ( options.numberField !== false ) {
1245 + const numberAttrs = {
1246 + block_id: Math.random().toString( 36 ).substring( 2, 9 ),
1247 + slug: 'quantity',
1248 + label: 'Quantity',
1249 + required: true,
1250 + minValue: options.minValue ?? 5,
1251 + maxValue: options.maxValue ?? 50,
1252 + formId,
1253 + };
1254 + if ( options.numberErrorMsg ) {
1255 + numberAttrs.errorMsg = options.numberErrorMsg;
1256 + }
1257 + const numberBlock = `<!-- wp:suredonation/number ${ JSON.stringify(
1258 + numberAttrs
1259 + ) } /-->`;
1260 +
1261 + if ( content.includes( '<!-- wp:suredonation/donate-button' ) ) {
1262 + content = content.replace(
1263 + '<!-- wp:suredonation/donate-button',
1264 + numberBlock + '\n<!-- wp:suredonation/donate-button'
1265 + );
1266 + } else {
1267 + content += '\n' + numberBlock;
1268 + }
1269 + }
1270 +
1271 + await requestUtils.rest( {
1272 + method: 'PUT',
1273 + path: `/wp/v2/${ FORM_CPT }/${ formId }`,
1274 + data: { content },
1275 + } );
1276 +
1277 + const page = await requestUtils.rest( {
1278 + method: 'POST',
1279 + path: '/wp/v2/pages',
1280 + data: {
1281 + title: `E2E Field Validation ${ Date.now() }`,
1282 + status: 'publish',
1283 + content: `[suredonation_form id="${ formId }"]`,
1284 + },
1285 + } );
1286 +
1287 + return { pageUrl: page.link, campaignId, formId, pageId: page.id };
1288 +}
1289 +
1290 +/**
997 1291 * Get PayPal settings via the plugin REST API.
998 1292 *
999 1293 * @param {Object} requestUtils Playwright requestUtils fixture.
1000 1294 * @return {Promise<Object>} API response with settings.
@@ -1183,9 +1477,9 @@
1183 1477 }
1184 1478
1185 1479 // Inject block_id (and formId for payment block) into ALL suredonation/* blocks.
1186 1480 updatedContent = updatedContent.replace(
1187 - /<!-- wp:sd\/(\w[\w-]*) \{(?!"block_id")/g,
1481 + /<!-- wp:suredonation\/(\w[\w-]*) \{(?!"block_id")/g,
1188 1482 ( match, blockName ) => {
1189 1483 const id = Math.random().toString( 36 ).substring( 2, 9 );
1190 1484 if ( blockName === 'payment' ) {
1191 1485 return `<!-- wp:suredonation/${ blockName } {"block_id":"${ id }","formId":${ formId },`;
@@ -1399,8 +1693,13 @@
1399 1693 }
1400 1694 }
1401 1695
1402 1696 module.exports = {
1697 + unwrapSettings,
1698 + uniqueEmail,
1699 + getSpamProtectionSettings,
1700 + enableHoneypot,
1701 + disableHoneypot,
1403 1702 createTestCampaign,
1404 1703 createTestDonation,
1405 1704 seedRecurringTestData,
1406 1705 cleanupTestData,
@@ -1412,8 +1711,10 @@
1412 1711 createOfflineFormPage,
1413 1712 cleanupOfflineFormPage,
1414 1713 seedOfflineTestData,
1415 1714 cleanupOfflineTestData,
1715 + getPaymentSettings,
1716 + setPaymentMode,
1416 1717 getFeeRecoverySettings,
1417 1718 updateFeeRecoverySettings,
1418 1719 createFeeRecoveryFormPage,
1419 1720 cleanupFeeRecoveryFormPage,
@@ -1429,5 +1730,8 @@
1429 1730 createPayPalFormPage,
1430 1731 cleanupPayPalFormPage,
1431 1732 seedDonorTestData,
1432 1733 cleanupDonorTestData,
1734 + getValidationSettings,
1735 + updateValidationSettings,
1736 + createValidationFormPage,
1433 1737 };