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
14 changes: 14 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@
"only": [
"wpackagist-plugin/*"
]
},
{
"type": "package",
"package": {
"name": "pronamic/woocommerce-subscriptions",
"version": "9.2.0",
"type": "wordpress-plugin",
"source": {
"type": "git",
"url": "https://github.com/pronamic/woocommerce-subscriptions.git",
"reference": "0a2c496cd263811707caa37c8bf01ffa450c6009"
}
}
}
],
"require": {
Expand All @@ -18,6 +31,7 @@
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8",
"composer/installers": "^2.3",
"pronamic/woocommerce-subscriptions": "^9.2",
"roots/wordpress": "^7.0",
"wp-phpunit/wp-phpunit": "^7.0",
"wpackagist-plugin/woocommerce": "^11.0"
Expand Down
12 changes: 11 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
require_once __DIR__ . '/inc/product.php';
require_once __DIR__ . '/inc/cpf-cnpj.php';
require_once __DIR__ . '/inc/checkout.php';
require_once __DIR__ . '/inc/onboarding.php';
19 changes: 2 additions & 17 deletions inc/account.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,8 @@ function libresign_theme_render_lost_password_form() {
* - Any other account page → WooCommerce my-account shortcode (which uses
* the woocommerce/myaccount/form-login.php template override for guests
* and the standard dashboard for logged-in users).
* - Shop / checkout → prepend the SaaS onboarding block pattern.
*/
function libresign_theme_prepend_saas_onboarding_to_content( $content ) {
function libresign_theme_render_account_content( $content ) {
if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
return $content;
}
Expand Down Expand Up @@ -178,23 +177,9 @@ function libresign_theme_prepend_saas_onboarding_to_content( $content ) {
return function_exists( 'do_shortcode' ) ? do_shortcode( '[woocommerce_my_account]' ) : $content;
}

$should_prepend = ( function_exists( 'is_shop' ) && is_shop() )
|| ( function_exists( 'is_checkout' ) && is_checkout() );

if ( function_exists( 'is_order_received_page' ) && is_order_received_page() ) {
$should_prepend = true;
}

if ( $should_prepend ) {
$onboarding_block = '<!-- wp:pattern {"slug":"libresign/saas-onboarding"} /-->';
if ( false === strpos( $content, 'libresign/saas-onboarding' ) ) {
$content = do_blocks( $onboarding_block ) . $content;
}
}

return $content;
}
add_filter( 'the_content', 'libresign_theme_prepend_saas_onboarding_to_content', 5 );
add_filter( 'the_content', 'libresign_theme_render_account_content', 5 );

// ---------------------------------------------------------------------------
// Direct /lost-password/ route handler
Expand Down
39 changes: 39 additions & 0 deletions inc/onboarding.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
/**
* SaaS onboarding: shown only while buying a new plan.
*
* @package libresign
*/

defined( 'ABSPATH' ) || exit;

function libresign_theme_show_saas_onboarding_only_on_new_purchases( $block_content, $block ) {
$class_names = explode( ' ', $block['attrs']['className'] ?? '' );
if ( ! in_array( 'libresign-saas-onboarding', $class_names, true ) ) {
return $block_content;
}

if ( function_exists( 'is_wc_endpoint_url' ) && is_wc_endpoint_url( 'order-pay' ) ) {
Comment thread
YvesCesar marked this conversation as resolved.
return '';
}

$order_id = absint( get_query_var( 'order-received' ) );
if ( $order_id && function_exists( 'wcs_order_contains_subscription' ) && wcs_order_contains_subscription( $order_id, array( 'renewal', 'switch', 'resubscribe' ) ) ) {
return '';
}

if ( libresign_theme_cart_continues_an_existing_subscription() ) {
return '';
}

return $block_content;
}
add_filter( 'render_block_core/group', 'libresign_theme_show_saas_onboarding_only_on_new_purchases', 10, 2 );

function libresign_theme_cart_continues_an_existing_subscription() {
if ( ! function_exists( 'wcs_cart_contains_renewal' ) ) {
return false;
}

return wcs_cart_contains_renewal() || wcs_cart_contains_resubscribe() || wcs_cart_contains_switches();
}
55 changes: 55 additions & 0 deletions patterns/saas-onboarding.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
* Title: SaaS onboarding
* Slug: libresign/saas-onboarding
* Inserter: no
*/
?>

<!-- wp:group {"align":"wide","className":"libresign-saas-onboarding","style":{"spacing":{"margin":{"bottom":"var:preset|spacing|40"}}},"layout":{"type":"default"}} -->
<div class="wp-block-group alignwide libresign-saas-onboarding" style="margin-bottom:var(--wp--preset--spacing--40)">
<!-- wp:heading {"level":2,"fontSize":"large"} -->
<h2 class="wp-block-heading has-large-font-size"><?php echo esc_html__( 'How your LibreSign workspace works', 'libresign' ); ?></h2>
<!-- /wp:heading -->

<!-- wp:columns -->
<div class="wp-block-columns">
<!-- wp:column -->
<div class="wp-block-column">
<!-- wp:heading {"level":3,"fontSize":"medium"} -->
<h3 class="wp-block-heading has-medium-font-size"><?php echo esc_html__( '1. Choose a plan', 'libresign' ); ?></h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p><?php echo esc_html__( 'Pick the plan that fits your team.', 'libresign' ); ?></p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column">
<!-- wp:heading {"level":3,"fontSize":"medium"} -->
<h3 class="wp-block-heading has-medium-font-size"><?php echo esc_html__( '2. Create your workspace', 'libresign' ); ?></h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p><?php echo esc_html__( 'Set your email and password and complete the payment.', 'libresign' ); ?></p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column">
<!-- wp:heading {"level":3,"fontSize":"medium"} -->
<h3 class="wp-block-heading has-medium-font-size"><?php echo esc_html__( '3. Start signing', 'libresign' ); ?></h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p><?php echo esc_html__( 'Sign in to LibreSign with the same email and password.', 'libresign' ); ?></p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:column -->
</div>
<!-- /wp:columns -->
</div>
<!-- /wp:group -->
5 changes: 5 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ parameters:
bootstrapFiles:
- vendor-bin/phpstan/vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php
- vendor-bin/phpunit/vendor/autoload.php
scanFiles:
- vendor/test-plugins/woocommerce-subscriptions/includes/core/wcs-functions.php
- vendor/test-plugins/woocommerce-subscriptions/includes/core/wcs-resubscribe-functions.php
- vendor/test-plugins/woocommerce-subscriptions/includes/core/wcs-switch-functions.php
- vendor/test-plugins/woocommerce-subscriptions/includes/core/abstracts/abstract-wcs-related-order-store.php
scanDirectories:
- vendor/wp-phpunit/wp-phpunit/includes
ignoreErrors:
Expand Down
7 changes: 4 additions & 3 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ test under `tests/E2E` follows the same names. `tests/Unit/StructureTest.php`
enforces this and also fails on a test whose file no longer exists. In CI, octocov fails the run when line
coverage is below 90% or below the last report of `main` (`.octocov.yml`).

`composer install` brings in WordPress, the WordPress test suite and WooCommerce,
so the tests only need a MySQL/MariaDB database they are allowed to wipe on every
`composer install` brings in WordPress, the WordPress test suite, WooCommerce and
WooCommerce Subscriptions, so the tests only need a MySQL/MariaDB database they are allowed to wipe on every
run. The connection comes from `WP_TESTS_DB_NAME` (`wordpress_test`),
`WP_TESTS_DB_USER` (`root`), `WP_TESTS_DB_PASSWORD` (`root`), `WP_TESTS_DB_HOST`
(`mariadb`) and `WP_TESTS_TABLE_PREFIX` (`wptests_`).
Expand All @@ -82,7 +82,8 @@ On the local SaaS stack:
wordpress-docker-wordpress-1 composer test

The browser tests in `tests/E2E` cover creating a workspace together with a plan,
the CPF/CNPJ field and the policy consent at checkout, and the lost password page.
the CPF/CNPJ field and the policy consent at checkout, the lost password page,
and the onboarding on the shop, checkout and order received pages.
They need Docker, Node.js and a `composer install`, since the stack mounts
WooCommerce from `vendor/test-plugins`:

Expand Down
2 changes: 2 additions & 0 deletions templates/archive-product.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

<!-- wp:group {"tagName":"main","layout":{"inherit":true,"type":"constrained"}} -->
<main class="wp-block-group">
<!-- wp:pattern {"slug":"libresign/saas-onboarding"} /-->

<!-- wp:woocommerce/breadcrumbs /-->

<!-- wp:query-title {"type":"archive","showPrefix":false,"align":"wide"} /-->
Expand Down
1 change: 1 addition & 0 deletions templates/page-checkout.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<!-- wp:woocommerce/page-content-wrapper {"page":"checkout"} -->
<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
<main class="wp-block-group">
<!-- wp:pattern {"slug":"libresign/saas-onboarding"} /-->
<!-- wp:woocommerce/store-notices /-->
<!-- wp:post-content {"align":"wide"} /-->
</main>
Expand Down
41 changes: 41 additions & 0 deletions tests/E2E/Patterns/SaasOnboarding.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { expect, Page, test } from '@playwright/test';

import { addThePlanToTheCart, fillTheBillingAddress, placeOrder } from '../support/store';

async function expectTheOnboarding( page: Page ): Promise< void > {
await expect( page.getByRole( 'heading', { name: 'How your LibreSign workspace works' } ) ).toHaveCount( 1 );
await expect( page.getByRole( 'heading', { name: '1. Choose a plan' } ) ).toBeVisible();
await expect( page.getByRole( 'heading', { name: '2. Create your workspace' } ) ).toBeVisible();
await expect( page.getByRole( 'heading', { name: '3. Start signing' } ) ).toBeVisible();
}

test.describe( 'Showing how the workspace works while buying', () => {
test( 'on the shop', async ( { page } ) => {
await page.goto( '/shop/' );

await expectTheOnboarding( page );
} );

test( 'on the checkout', async ( { page } ) => {
await addThePlanToTheCart( page );
await page.goto( '/checkout/' );

await expectTheOnboarding( page );
} );

test( 'on the order received', async ( { page } ) => {
await addThePlanToTheCart( page );
await fillTheBillingAddress( page, 'Brazil', '529.982.247-25' );
await page.getByLabel( 'I agree to the terms and privacy policy before placing the order.' ).check();
await placeOrder( page );

await expect( page ).toHaveURL( /\/order-received\// );
await expectTheOnboarding( page );
} );

test( 'not on other pages', async ( { page } ) => {
await page.goto( '/product/basic/' );

await expect( page.getByRole( 'heading', { name: 'How your LibreSign workspace works' } ) ).toHaveCount( 0 );
} );
} );
8 changes: 4 additions & 4 deletions tests/Integration/Inc/AccountTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,22 +165,22 @@ public function test_the_account_page_leaves_the_reset_confirmation_to_woocommer
public function test_keeps_the_content_of_other_pages() {
$this->in_the_loop_of( home_url() );

$this->assertSame( '<p>Page</p>', libresign_theme_prepend_saas_onboarding_to_content( '<p>Page</p>' ) );
$this->assertSame( '<p>Page</p>', libresign_theme_render_account_content( '<p>Page</p>' ) );
}

public function test_keeps_the_content_outside_the_loop() {
add_filter( 'woocommerce_is_account_page', '__return_true' );
$this->go_to( home_url() );

$this->assertSame( '<p>Page</p>', libresign_theme_prepend_saas_onboarding_to_content( '<p>Page</p>' ) );
$this->assertSame( '<p>Page</p>', libresign_theme_render_account_content( '<p>Page</p>' ) );
}

public function test_keeps_the_content_in_the_admin() {
add_filter( 'woocommerce_is_account_page', '__return_true' );
$this->in_the_loop_of( home_url() );
set_current_screen( 'dashboard' );

$this->assertSame( '<p>Page</p>', libresign_theme_prepend_saas_onboarding_to_content( '<p>Page</p>' ) );
$this->assertSame( '<p>Page</p>', libresign_theme_render_account_content( '<p>Page</p>' ) );
}

/**
Expand All @@ -206,7 +206,7 @@ private function account_page_content( $query = array() ) {
add_filter( 'woocommerce_is_account_page', '__return_true' );
$this->in_the_loop_of( add_query_arg( $query, home_url( '/' ) ) );

return libresign_theme_prepend_saas_onboarding_to_content( '' );
return libresign_theme_render_account_content( '' );
}

private function in_the_loop_of( $url ) {
Expand Down
Loading
Loading