From a2085f2cee6f69902c493c43aa7c517c93f83d73 Mon Sep 17 00:00:00 2001 From: Alex Worrad-Andrews Date: Sat, 28 Mar 2026 21:34:11 +0000 Subject: [PATCH 1/5] Add tests documenting allow_cards_override expected frontend behaviour --- packages/join-flow/src/env.test.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/join-flow/src/env.test.ts b/packages/join-flow/src/env.test.ts index 098a755..50b28d6 100644 --- a/packages/join-flow/src/env.test.ts +++ b/packages/join-flow/src/env.test.ts @@ -42,6 +42,20 @@ describe('getPaymentProviders — Stripe direct debit flags', () => { expect(getPaymentProviders().stripe).toEqual(['creditCard']); }); }); + + it('STRIPE_DIRECT_DEBIT_ONLY explicitly false (allow_cards_override) produces creditCard only', () => { + withEnv({ USE_STRIPE: true, STRIPE_DIRECT_DEBIT_ONLY: false }, () => { + expect(getPaymentProviders().stripe).toEqual(['creditCard']); + }); + }); + + it('STRIPE_DIRECT_DEBIT_ONLY explicitly false with STRIPE_DIRECT_DEBIT true produces both methods', () => { + withEnv({ USE_STRIPE: true, STRIPE_DIRECT_DEBIT_ONLY: false, STRIPE_DIRECT_DEBIT: true }, () => { + const methods = getPaymentProviders().stripe; + expect(methods).toContain('creditCard'); + expect(methods).toContain('directDebit'); + }); + }); }); describe('getPaymentMethods — STRIPE_DIRECT_DEBIT_ONLY', () => { @@ -90,4 +104,10 @@ describe('resolveStripePaymentMethodTypes', () => { expect(resolveStripePaymentMethodTypes(false, 'eur')).toEqual(['card']); }); }); + + it('subscription with STRIPE_DIRECT_DEBIT_ONLY explicitly false (allow_cards_override) returns card-only', () => { + withEnv({ STRIPE_DIRECT_DEBIT_ONLY: false }, () => { + expect(resolveStripePaymentMethodTypes(false, 'gbp')).toEqual(['card']); + }); + }); }); From 983e248372ea23a7b45a74a33d2cfc3b9f69fdb6 Mon Sep 17 00:00:00 2001 From: Alex Worrad-Andrews Date: Sat, 28 Mar 2026 21:34:42 +0000 Subject: [PATCH 2/5] Allow cards on individual forms when Direct Debit Only is set globally (JOIN-142) --- packages/join-block/src/Blocks.php | 95 +++++++++++++++++------------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/packages/join-block/src/Blocks.php b/packages/join-block/src/Blocks.php index 9296ada..150a207 100644 --- a/packages/join-block/src/Blocks.php +++ b/packages/join-block/src/Blocks.php @@ -168,49 +168,58 @@ private static function registerJoinFormBlock() $custom_fields = self::createCustomFieldsField(); + $block_fields = [ + Field::make('separator', 'ck_join_form', 'CK Join Form'), + $joined_page_association, + Field::make('checkbox', 'require_address')->set_default_value(true), + Field::make('checkbox', 'hide_address') + ->set_help_text('Check to completely hide the address section from the form.'), + Field::make('checkbox', 'require_phone_number')->set_default_value(true), + Field::make('checkbox', 'ask_for_additional_donation') + ->set_help_text('Has no effect when Donation Supporter Mode is enabled.'), + Field::make('checkbox', 'donation_supporter_mode') + ->set_help_text( + 'Enable Supporter Mode: shows donation frequency and amount first, ' . + 'before personal details and payment. Skips the membership plan step. ' . + 'Requires block-level membership plans to be configured (used as donation tiers). ' . + 'One-off donations require Stripe. They are not available with Direct Debit only.' + ), + Field::make('checkbox', 'hide_home_address_copy') + ->set_help_text('Check to hide the copy that explains why the address is collected.'), + Field::make('checkbox', 'include_skip_payment_button') + ->set_help_text( + 'Check to include an additional button on the first page to skip to the thank you page ' . + '(which could include a form for additional questions)' + ), + Field::make('checkbox', 'is_update_flow', 'Is Update Flow (e.g. for existing members)') + ->set_help_text( + 'Check to skip collecting member details (e.g. name, address). If checked, this page must ' . + 'be linked to with the email URL search parameter set, e.g. /become-paid-member/?email=someone@example.com. ' . + 'This can be achieved by using the CK Join Form Link block on a landing page, and linking to this page.' + ), + $custom_fields, + $custom_membership_plans, + Field::make('text', 'custom_webhook_url') + ->set_help_text('Leave blank to use the default Join Complete webhook from the settings page.'), + Field::make('text', 'custom_sidebar_heading') + ->set_help_text('Leave blank to use the default from settings page.'), + Field::make('text', 'custom_membership_stage_label') + ->set_help_text('Leave blank to use the default from settings page.'), + Field::make('text', 'custom_joining_verb') + ->set_help_text('Leave blank to use the default from settings page (e.g., "Joining").'), + ]; + + if (Settings::get("STRIPE_DIRECT_DEBIT_ONLY")) { + $block_fields[] = Field::make('checkbox', 'allow_cards_override') + ->set_help_text( + 'Override the global "Direct Debit Only" setting for this block, ' . + 'allowing card payments alongside direct debit.' + ); + } + /** @var Block_Container $join_form_block */ $join_form_block = Block::make(__('CK Join Form', 'common-knowledge-join-flow')) - ->add_fields(array( - Field::make('separator', 'ck_join_form', 'CK Join Form'), - $joined_page_association, - Field::make('checkbox', 'require_address')->set_default_value(true), - Field::make('checkbox', 'hide_address') - ->set_help_text('Check to completely hide the address section from the form.'), - Field::make('checkbox', 'require_phone_number')->set_default_value(true), - Field::make('checkbox', 'ask_for_additional_donation') - ->set_help_text('Has no effect when Donation Supporter Mode is enabled.'), - Field::make('checkbox', 'donation_supporter_mode') - ->set_help_text( - 'Enable Supporter Mode: shows donation frequency and amount first, ' . - 'before personal details and payment. Skips the membership plan step. ' . - 'Requires block-level membership plans to be configured (used as donation tiers). ' . - 'One-off donations require Stripe. They are not available with Direct Debit only.' - ), - Field::make('checkbox', 'hide_home_address_copy') - ->set_help_text('Check to hide the copy that explains why the address is collected.'), - Field::make('checkbox', 'include_skip_payment_button') - ->set_help_text( - 'Check to include an additional button on the first page to skip to the thank you page ' . - '(which could include a form for additional questions)' - ), - Field::make('checkbox', 'is_update_flow', 'Is Update Flow (e.g. for existing members)') - ->set_help_text( - 'Check to skip collecting member details (e.g. name, address). If checked, this page must ' . - 'be linked to with the email URL search parameter set, e.g. /become-paid-member/?email=someone@example.com. ' . - 'This can be achieved by using the CK Join Form Link block on a landing page, and linking to this page.' - ), - $custom_fields, - $custom_membership_plans, - Field::make('text', 'custom_webhook_url') - ->set_help_text('Leave blank to use the default Join Complete webhook from the settings page.'), - Field::make('text', 'custom_sidebar_heading') - ->set_help_text('Leave blank to use the default from settings page.'), - Field::make('text', 'custom_membership_stage_label') - ->set_help_text('Leave blank to use the default from settings page.'), - Field::make('text', 'custom_joining_verb') - ->set_help_text('Leave blank to use the default from settings page (e.g., "Joining").'), - - )); + ->add_fields($block_fields); $join_form_block->set_render_callback(function ($fields, $attributes, $inner_blocks) { self::enqueueBlockCss(); self::echoEnvironment($fields, self::NORMAL_BLOCK_MODE); @@ -463,7 +472,9 @@ function ($o) { "REQUIRE_PHONE_NUMBER" => $fields["require_phone_number"] ?? true, "SENTRY_DSN" => Settings::get("SENTRY_DSN"), "STRIPE_DIRECT_DEBIT" => Settings::get("STRIPE_DIRECT_DEBIT"), - "STRIPE_DIRECT_DEBIT_ONLY" => Settings::get("STRIPE_DIRECT_DEBIT_ONLY"), + "STRIPE_DIRECT_DEBIT_ONLY" => !empty($fields['allow_cards_override']) + ? false + : Settings::get("STRIPE_DIRECT_DEBIT_ONLY"), "STRIPE_PUBLISHABLE_KEY" => Settings::get("STRIPE_PUBLISHABLE_KEY"), "SUBSCRIPTION_DAY_OF_MONTH_COPY" => Settings::get("SUBSCRIPTION_DAY_OF_MONTH_COPY"), "USE_CHARGEBEE" => Settings::get("USE_CHARGEBEE"), From c7dc855b438a1f32d8b53d5109e4611b8ba6261d Mon Sep 17 00:00:00 2001 From: Alex Worrad-Andrews Date: Sat, 28 Mar 2026 21:35:05 +0000 Subject: [PATCH 3/5] Bump version to 1.4.1 --- packages/join-block/join.php | 2 +- packages/join-block/readme.txt | 4 +++- packages/join-flow/src/index.tsx | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/join-block/join.php b/packages/join-block/join.php index 0410cb3..a7950b8 100644 --- a/packages/join-block/join.php +++ b/packages/join-block/join.php @@ -3,7 +3,7 @@ /** * Plugin Name: Common Knowledge Join Flow * Description: Common Knowledge join flow plugin. - * Version: 1.4.0 + * Version: 1.4.1 * Author: Common Knowledge * Text Domain: common-knowledge-join-flow * License: GPLv2 or later diff --git a/packages/join-block/readme.txt b/packages/join-block/readme.txt index 2c6663e..fb3ec17 100644 --- a/packages/join-block/readme.txt +++ b/packages/join-block/readme.txt @@ -4,7 +4,7 @@ Tags: membership, subscription, join Contributors: commonknowledgecoop Requires at least: 5.4 Tested up to: 6.8 -Stable tag: 1.4.0 +Stable tag: 1.4.1 Requires PHP: 8.1 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -107,6 +107,8 @@ Need help? Contact us at [hello@commonknowledge.coop](mailto:hello@commonknowled == Changelog == += 1.4.1 = +* Allow cards on individual forms when Direct Debit Only is set globally = 1.4.0 = * Add Donation Supporter Mode: a new block setting that puts donation first, before personal details and payment, skipping the membership plan step entirely * Supporter mode: donation frequency (monthly/one-off) and tier selection driven by block-level membership plans diff --git a/packages/join-flow/src/index.tsx b/packages/join-flow/src/index.tsx index 1ef613a..5300a8f 100644 --- a/packages/join-flow/src/index.tsx +++ b/packages/join-flow/src/index.tsx @@ -24,7 +24,7 @@ const init = () => { const sentryDsn = getEnvStr("SENTRY_DSN") Sentry.init({ dsn: sentryDsn, - release: "1.4.0" + release: "1.4.1" }); if (getEnv('USE_CHARGEBEE')) { From f328d56f93681b5bf19b5a02ad1ec11d2aefb325 Mon Sep 17 00:00:00 2001 From: Alex Worrad-Andrews Date: Mon, 30 Mar 2026 11:46:51 +0100 Subject: [PATCH 4/5] Add e2e tests for allow_cards_override feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests verify the full-stack behaviour of the per-block card payment override introduced in this PR: 1. PHP env script: page with allow_cards_override emits STRIPE_DIRECT_DEBIT_ONLY: false; control page emits true. No injectEnvOverrides — reads raw PHP output. 2. One-off tab enabled on the override page, disabled without it. USE_STRIPE is injected; STRIPE_DIRECT_DEBIT_ONLY is left to the real PHP output so the assertion is a genuine full-stack check. 3. /join body for a one-off donation on the override page contains recurDonation=false, donationAmount>0, paymentMethod=creditCard. setup.php creates the e2e-allow-cards-override-supporter seed page and sets STRIPE_DIRECT_DEBIT_ONLY=true globally via carbon_set_theme_option so that allow_cards_override has something observable to override. --- packages/join-e2e/scripts/setup.php | 248 ++++++++++++++++++ .../tests/allow-cards-override.spec.ts | 110 ++++++++ 2 files changed, 358 insertions(+) create mode 100644 packages/join-e2e/scripts/setup.php create mode 100644 packages/join-e2e/tests/allow-cards-override.spec.ts diff --git a/packages/join-e2e/scripts/setup.php b/packages/join-e2e/scripts/setup.php new file mode 100644 index 0000000..40fb1d7 --- /dev/null +++ b/packages/join-e2e/scripts/setup.php @@ -0,0 +1,248 @@ + $plans, + 'require_address' => true, + 'require_phone_number' => true, + 'ask_for_additional_donation' => false, + ], + $overrides + ); + $attrs = wp_json_encode(['data' => $data]); + return "\n" + . '
' . "\n" + . ''; +} + +/** + * Create or update a page by post_name slug. + * Returns the page ID on success; exits with status 1 on failure. + */ +function ck_e2e_upsert_page(string $slug, string $title, string $content): int +{ + $existing = get_posts([ + 'post_type' => 'page', + 'post_status' => 'publish', + 'name' => $slug, + 'numberposts' => 1, + ]); + + if ($existing) { + $page_id = $existing[0]->ID; + wp_update_post(['ID' => $page_id, 'post_content' => $content]); + echo "Updated page '{$slug}' (ID: {$page_id}).\n"; + return $page_id; + } + + $page_id = wp_insert_post([ + 'post_name' => $slug, + 'post_title' => $title, + 'post_status' => 'publish', + 'post_type' => 'page', + 'post_content' => $content, + ], true); + + if (is_wp_error($page_id)) { + echo 'Failed to create page \'' . $slug . '\': ' . $page_id->get_error_message() . "\n"; + exit(1); + } + + echo "Created page '{$slug}' (ID: {$page_id}).\n"; + return $page_id; +} + +// Configure pretty permalinks so test URLs are predictable. +update_option('permalink_structure', '/%postname%/'); +flush_rewrite_rules(true); + +// Standard membership plan (£5/month). +$standard_plans = [ + [ + '_type' => '', + 'label' => 'Standard', + 'id' => 'standard', + 'amount' => '5', + 'allow_custom_amount' => '', + 'frequency' => 'monthly', + 'currency' => 'GBP', + 'description' => '', + 'add_tags' => '', + 'remove_tags' => '', + ], +]; + +// Free membership plan (£0/month). +$free_plans = [ + [ + '_type' => '', + 'label' => 'Free', + 'id' => 'free', + 'amount' => '0', + 'allow_custom_amount' => '', + 'frequency' => 'monthly', + 'currency' => 'GBP', + 'description' => '', + 'add_tags' => '', + 'remove_tags' => '', + ], +]; + +// Supporter mode donation tiers (£5, £10, £20/month). +$supporter_plans = [ + [ + '_type' => '', + 'label' => 'Supporter', + 'id' => 'supporter', + 'amount' => '5', + 'allow_custom_amount' => '', + 'frequency' => 'monthly', + 'currency' => 'GBP', + 'description' => '', + 'add_tags' => '', + 'remove_tags' => '', + ], + [ + '_type' => '', + 'label' => 'Friend', + 'id' => 'friend', + 'amount' => '10', + 'allow_custom_amount' => '', + 'frequency' => 'monthly', + 'currency' => 'GBP', + 'description' => '', + 'add_tags' => '', + 'remove_tags' => '', + ], + [ + '_type' => '', + 'label' => 'Patron', + 'id' => 'patron', + 'amount' => '20', + 'allow_custom_amount' => '', + 'frequency' => 'monthly', + 'currency' => 'GBP', + 'description' => '', + 'add_tags' => '', + 'remove_tags' => '', + ], +]; + +// Supporter mode plan with custom amount allowed. +$supporter_custom_plans = [ + [ + '_type' => '', + 'label' => 'Custom', + 'id' => 'custom', + 'amount' => '5', + 'allow_custom_amount' => '1', + 'frequency' => 'monthly', + 'currency' => 'GBP', + 'description' => '', + 'add_tags' => '', + 'remove_tags' => '', + ], +]; + +// Donation upsell plan (standard £5/month with upsell enabled). +$standard_upsell_plans = $standard_plans; + +$standard_page_id = ck_e2e_upsert_page( + 'e2e-standard-join', + 'E2E Standard Join Test', + ck_e2e_make_block_content($standard_plans) +); + +$free_page_id = ck_e2e_upsert_page( + 'e2e-free-join', + 'E2E Free Membership Test', + ck_e2e_make_block_content($free_plans) +); + +// Donation upsell page: standard join with ask_for_additional_donation enabled. +$donation_upsell_page_id = ck_e2e_upsert_page( + 'e2e-donation-upsell', + 'E2E Donation Upsell Test', + ck_e2e_make_block_content($standard_upsell_plans, ['ask_for_additional_donation' => true]) +); + +// Supporter mode page: donation-first flow with preset tiers. +$supporter_page_id = ck_e2e_upsert_page( + 'e2e-supporter', + 'E2E Supporter Mode Test', + ck_e2e_make_block_content($supporter_plans, ['donation_supporter_mode' => true]) +); + +// Supporter mode page with a custom-amount tier. +$supporter_custom_page_id = ck_e2e_upsert_page( + 'e2e-supporter-custom', + 'E2E Supporter Custom Amount Test', + ck_e2e_make_block_content($supporter_custom_plans, ['donation_supporter_mode' => true]) +); + +// Supporter mode page with no plans (to trigger the "no amounts configured" warning). +$supporter_no_plans_page_id = ck_e2e_upsert_page( + 'e2e-supporter-no-plans', + 'E2E Supporter No Plans Test', + ck_e2e_make_block_content([], ['donation_supporter_mode' => true]) +); + +// Supporter mode page with allow_cards_override enabled. +// Used to test that a per-block override of the global STRIPE_DIRECT_DEBIT_ONLY +// setting is reflected in the env JSON emitted by the PHP render callback. +$allow_cards_override_page_id = ck_e2e_upsert_page( + 'e2e-allow-cards-override-supporter', + 'E2E Allow Cards Override Test', + ck_e2e_make_block_content($supporter_plans, [ + 'donation_supporter_mode' => true, + 'allow_cards_override' => true, + ]) +); + +// Enable STRIPE_DIRECT_DEBIT_ONLY globally so that allow_cards_override has +// something to override. Without this the global default is false and the +// override would produce no observable difference. +carbon_set_theme_option('stripe_direct_debit_only', true); + +// Persist URLs as options so get-page-url.sh can retrieve them. +update_option('ck_e2e_standard_page_url', get_permalink($standard_page_id)); +update_option('ck_e2e_free_page_url', get_permalink($free_page_id)); +update_option('ck_e2e_donation_upsell_page_url', get_permalink($donation_upsell_page_id)); +update_option('ck_e2e_supporter_page_url', get_permalink($supporter_page_id)); +update_option('ck_e2e_supporter_custom_page_url', get_permalink($supporter_custom_page_id)); +update_option('ck_e2e_supporter_no_plans_page_url', get_permalink($supporter_no_plans_page_id)); +update_option('ck_e2e_allow_cards_override_page_url', get_permalink($allow_cards_override_page_id)); + +echo 'Standard page URL: ' . get_permalink($standard_page_id) . "\n"; +echo 'Free page URL: ' . get_permalink($free_page_id) . "\n"; +echo 'Donation upsell page URL: ' . get_permalink($donation_upsell_page_id) . "\n"; +echo 'Supporter page URL: ' . get_permalink($supporter_page_id) . "\n"; +echo 'Supporter custom page URL: ' . get_permalink($supporter_custom_page_id) . "\n"; +echo 'Supporter no-plans page URL: ' . get_permalink($supporter_no_plans_page_id) . "\n"; +echo 'Allow cards override page URL: ' . get_permalink($allow_cards_override_page_id) . "\n"; +echo "Setup complete.\n"; diff --git a/packages/join-e2e/tests/allow-cards-override.spec.ts b/packages/join-e2e/tests/allow-cards-override.spec.ts new file mode 100644 index 0000000..1b79347 --- /dev/null +++ b/packages/join-e2e/tests/allow-cards-override.spec.ts @@ -0,0 +1,110 @@ +import { test, expect } from '@playwright/test'; +import { mockRestEndpoints, captureJoinBodyViaStripeRedirect, injectEnvOverrides, CONTINUE } from './helpers'; + +/** + * allow_cards_override — per-block card payment override + * + * When the global "Direct Debit Only" Stripe setting is enabled + * (STRIPE_DIRECT_DEBIT_ONLY=true), the block editor exposes an + * "allow_cards_override" checkbox. Checking it causes the PHP render + * callback to emit STRIPE_DIRECT_DEBIT_ONLY: false in the page's env JSON, + * re-enabling card (Stripe Elements) payments for that specific form instance. + * + * The seed script (setup.php) sets STRIPE_DIRECT_DEBIT_ONLY=true globally via + * carbon_set_theme_option and creates: + * - e2e-allow-cards-override-supporter: supporter mode + allow_cards_override=true + * - e2e-supporter: supporter mode, no override (global DD-only applies) + * + * Tests verify: + * 1. The PHP env script output is correct (full-stack, no injectEnvOverrides). + * 2. The one-off tab is enabled on the override page and disabled without it. + * 3. The /join request body for a one-off donation on the override page is correct. + * + * For behavioural tests, USE_STRIPE=true is injected (a global plugin setting + * unavailable in the test environment) but STRIPE_DIRECT_DEBIT_ONLY is NOT + * injected — it is left to the real PHP output to supply the value, which is + * what these tests are exercising. + */ + +const OVERRIDE_PAGE = '/e2e-allow-cards-override-supporter/'; +const SUPPORTER_PAGE = '/e2e-supporter/'; + +// --------------------------------------------------------------------------- +// PHP env script output — no injectEnvOverrides, reads raw PHP output +// --------------------------------------------------------------------------- + +test.describe('PHP env script: allow_cards_override overrides STRIPE_DIRECT_DEBIT_ONLY', () => { + test('page with allow_cards_override emits STRIPE_DIRECT_DEBIT_ONLY: false', async ({ page }) => { + await page.goto(OVERRIDE_PAGE); + const envJson = await page.locator('script#env').textContent(); + const env = JSON.parse(envJson || '{}'); + // PHP must have emitted false (not the global true) because allow_cards_override=true. + expect(env.STRIPE_DIRECT_DEBIT_ONLY).toBe(false); + }); + + test('page without allow_cards_override emits STRIPE_DIRECT_DEBIT_ONLY: true (global applies)', async ({ page }) => { + await page.goto(SUPPORTER_PAGE); + const envJson = await page.locator('script#env').textContent(); + const env = JSON.parse(envJson || '{}'); + // No override — the global STRIPE_DIRECT_DEBIT_ONLY=true must be present. + expect(env.STRIPE_DIRECT_DEBIT_ONLY).toBe(true); + }); +}); + +// --------------------------------------------------------------------------- +// One-off tab availability — STRIPE_DIRECT_DEBIT_ONLY supplied by real PHP +// --------------------------------------------------------------------------- + +test.describe('One-off tab enabled on override page, disabled without it', () => { + test('one-off tab is enabled on page with allow_cards_override', async ({ page }) => { + // Inject USE_STRIPE=true (global plugin setting not available in test env). + // STRIPE_DIRECT_DEBIT_ONLY is intentionally NOT injected — it comes from PHP. + await injectEnvOverrides(page, `**${OVERRIDE_PAGE}`, { USE_STRIPE: true }); + await mockRestEndpoints(page); + await page.goto(OVERRIDE_PAGE); + await page.waitForSelector('h2:has-text("Support us")'); + + const oneOffBtn = page.locator('.btn-group button:has-text("One-off")'); + await expect(oneOffBtn).toBeVisible(); + await expect(oneOffBtn).toBeEnabled(); + }); + + test('one-off tab is disabled on page without allow_cards_override (global DD-only applies)', async ({ page }) => { + await injectEnvOverrides(page, `**${SUPPORTER_PAGE}`, { USE_STRIPE: true }); + await mockRestEndpoints(page); + await page.goto(SUPPORTER_PAGE); + await page.waitForSelector('h2:has-text("Support us")'); + + const oneOffBtn = page.locator('.btn-group button:has-text("One-off")'); + await expect(oneOffBtn).toBeVisible(); + await expect(oneOffBtn).toBeDisabled(); + }); +}); + +// --------------------------------------------------------------------------- +// /join request body for a one-off donation via the override page +// --------------------------------------------------------------------------- + +test.describe('/join body for one-off donation on override page', () => { + test('recurDonation=false and donationAmount>0 in /join body', async ({ page }) => { + await injectEnvOverrides(page, `**${OVERRIDE_PAGE}`, { USE_STRIPE: true }); + await mockRestEndpoints(page); + await page.goto(OVERRIDE_PAGE); + await page.waitForSelector('h2:has-text("Support us")'); + + // Select one-off, pick a tier, advance through details. + await page.locator('.btn-group button:has-text("One-off")').click(); + await page.locator('button[type="button"]:has-text("£5")').click(); + await page.locator('button[type="submit"]').click(); + await page.waitForSelector('input#firstName'); + await page.locator(CONTINUE).click(); + + const joinBody = await captureJoinBodyViaStripeRedirect(page, OVERRIDE_PAGE); + + expect(Object.keys(joinBody).length).toBeGreaterThan(0); + expect(joinBody.recurDonation).toBe(false); + expect(Number(joinBody.donationAmount)).toBeGreaterThan(0); + // paymentMethod must be creditCard (the allow_cards_override unlocks card for one-off). + expect(joinBody.paymentMethod).toBe('creditCard'); + }); +}); From 3c1229ff7e3787e1defc404cee0f5c70bc68bbbc Mon Sep 17 00:00:00 2001 From: Alex Andrews Date: Mon, 30 Mar 2026 11:52:50 +0100 Subject: [PATCH 5/5] Use correct casing of Direct Debit --- packages/join-block/src/Blocks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/join-block/src/Blocks.php b/packages/join-block/src/Blocks.php index 150a207..f1d1a11 100644 --- a/packages/join-block/src/Blocks.php +++ b/packages/join-block/src/Blocks.php @@ -213,7 +213,7 @@ private static function registerJoinFormBlock() $block_fields[] = Field::make('checkbox', 'allow_cards_override') ->set_help_text( 'Override the global "Direct Debit Only" setting for this block, ' . - 'allowing card payments alongside direct debit.' + 'allowing card payments alongside Direct Debit.' ); }