@ai-kits/wp-ag-kit 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -14
- package/STRUCTURE.md +9 -7
- package/agents/woocommerce-expert.md +32 -0
- package/agents/wordpress-expert.md +4 -2
- package/package.json +5 -3
- package/skills/woocommerce/SKILL.md +54 -0
- package/skills/woocommerce/references/backend-dev-guide.md +44 -0
- package/skills/woocommerce/references/code-entities.md +186 -0
- package/skills/woocommerce/references/code-quality.md +273 -0
- package/skills/woocommerce/references/code-review-guide.md +71 -0
- package/skills/woocommerce/references/coding-conventions.md +182 -0
- package/skills/woocommerce/references/copy-guidelines-guide.md +17 -0
- package/skills/woocommerce/references/data-integrity.md +164 -0
- package/skills/woocommerce/references/dependency-injection.md +102 -0
- package/skills/woocommerce/references/dev-cycle-guide.md +32 -0
- package/skills/woocommerce/references/file-entities.md +73 -0
- package/skills/woocommerce/references/hooks.md +87 -0
- package/skills/woocommerce/references/js-i18n-patterns.md +298 -0
- package/skills/woocommerce/references/markdown-guide.md +358 -0
- package/skills/woocommerce/references/markdown-linting.md +202 -0
- package/skills/woocommerce/references/php-i18n-patterns.md +83 -0
- package/skills/woocommerce/references/php-linting-patterns.md +304 -0
- package/skills/woocommerce/references/running-tests.md +249 -0
- package/skills/woocommerce/references/security-patterns.md +109 -0
- package/skills/woocommerce/references/sentence-case.md +177 -0
- package/skills/woocommerce/references/type-annotations.md +161 -0
- package/skills/woocommerce/references/unit-tests.md +362 -0
- package/skills/woocommerce/references/woocommerce-global-objects.md +89 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Sentence Case for UI Text
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Rule: Always Use Sentence Case](#rule-always-use-sentence-case)
|
|
6
|
+
- [Examples](#examples)
|
|
7
|
+
- [Exceptions](#exceptions)
|
|
8
|
+
- [Common UI Elements](#common-ui-elements)
|
|
9
|
+
- [Why Sentence Case?](#why-sentence-case)
|
|
10
|
+
- [Quick Reference](#quick-reference)
|
|
11
|
+
- [When in Doubt](#when-in-doubt)
|
|
12
|
+
|
|
13
|
+
## Rule: Always Use Sentence Case
|
|
14
|
+
|
|
15
|
+
**Always use sentence case for UI copy, not title case.**
|
|
16
|
+
|
|
17
|
+
Sentence case means only the first word and proper nouns are capitalized.
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
### Correct - Sentence Case
|
|
22
|
+
|
|
23
|
+
- "Payment provider options"
|
|
24
|
+
- "Complete setup"
|
|
25
|
+
- "Add new gateway"
|
|
26
|
+
- "Configure payment settings"
|
|
27
|
+
- "Save changes"
|
|
28
|
+
- "View documentation"
|
|
29
|
+
- "Enable test mode"
|
|
30
|
+
|
|
31
|
+
### Incorrect - Title Case
|
|
32
|
+
|
|
33
|
+
- "Payment Provider Options"
|
|
34
|
+
- "Complete Setup"
|
|
35
|
+
- "Add New Gateway"
|
|
36
|
+
- "Configure Payment Settings"
|
|
37
|
+
- "Save Changes"
|
|
38
|
+
- "View Documentation"
|
|
39
|
+
- "Enable Test Mode"
|
|
40
|
+
|
|
41
|
+
## Exceptions
|
|
42
|
+
|
|
43
|
+
Sentence case has specific exceptions where capitalization is required:
|
|
44
|
+
|
|
45
|
+
### 1. Proper Nouns
|
|
46
|
+
|
|
47
|
+
Always capitalize proper nouns (names of specific products, services, or brands):
|
|
48
|
+
|
|
49
|
+
**Correct:**
|
|
50
|
+
|
|
51
|
+
- "Connect with WooPayments"
|
|
52
|
+
- "Import from WordPress"
|
|
53
|
+
- "Sync with Stripe"
|
|
54
|
+
- "Enable Google Analytics"
|
|
55
|
+
|
|
56
|
+
### 2. Acronyms
|
|
57
|
+
|
|
58
|
+
Always capitalize acronyms:
|
|
59
|
+
|
|
60
|
+
**Correct:**
|
|
61
|
+
|
|
62
|
+
- "Configure API settings"
|
|
63
|
+
- "Enter URL"
|
|
64
|
+
- "Set up SSL certificate"
|
|
65
|
+
- "Generate CSV export"
|
|
66
|
+
|
|
67
|
+
### 3. Brand Names
|
|
68
|
+
|
|
69
|
+
Always use the official capitalization for brand names:
|
|
70
|
+
|
|
71
|
+
**Correct:**
|
|
72
|
+
|
|
73
|
+
- "PayPal"
|
|
74
|
+
- "WooCommerce"
|
|
75
|
+
- "WordPress"
|
|
76
|
+
- "Stripe"
|
|
77
|
+
|
|
78
|
+
## Common UI Elements
|
|
79
|
+
|
|
80
|
+
### Buttons
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
✅ Correct:
|
|
84
|
+
- "Save changes"
|
|
85
|
+
- "Add product"
|
|
86
|
+
- "Continue to checkout"
|
|
87
|
+
- "Send test email"
|
|
88
|
+
|
|
89
|
+
❌ Incorrect:
|
|
90
|
+
- "Save Changes"
|
|
91
|
+
- "Add Product"
|
|
92
|
+
- "Continue To Checkout"
|
|
93
|
+
- "Send Test Email"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Labels
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
✅ Correct:
|
|
100
|
+
- "Payment method"
|
|
101
|
+
- "Shipping address"
|
|
102
|
+
- "Order notes"
|
|
103
|
+
- "Tax rate"
|
|
104
|
+
|
|
105
|
+
❌ Incorrect:
|
|
106
|
+
- "Payment Method"
|
|
107
|
+
- "Shipping Address"
|
|
108
|
+
- "Order Notes"
|
|
109
|
+
- "Tax Rate"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Menu Items
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
✅ Correct:
|
|
116
|
+
- "Settings"
|
|
117
|
+
- "Payment gateways"
|
|
118
|
+
- "Tax settings"
|
|
119
|
+
- "Shipping zones"
|
|
120
|
+
|
|
121
|
+
❌ Incorrect:
|
|
122
|
+
- "Settings" (this one is actually correct - it's a single word)
|
|
123
|
+
- "Payment Gateways"
|
|
124
|
+
- "Tax Settings"
|
|
125
|
+
- "Shipping Zones"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Headings
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
✅ Correct:
|
|
132
|
+
- "Payment settings"
|
|
133
|
+
- "Configure your store"
|
|
134
|
+
- "Advanced options"
|
|
135
|
+
|
|
136
|
+
❌ Incorrect:
|
|
137
|
+
- "Payment Settings"
|
|
138
|
+
- "Configure Your Store"
|
|
139
|
+
- "Advanced Options"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Messages and Notifications
|
|
143
|
+
|
|
144
|
+
```text
|
|
145
|
+
✅ Correct:
|
|
146
|
+
- "Settings saved successfully"
|
|
147
|
+
- "Unable to connect to payment provider"
|
|
148
|
+
- "Please enter a valid email address"
|
|
149
|
+
|
|
150
|
+
❌ Incorrect:
|
|
151
|
+
- "Settings Saved Successfully"
|
|
152
|
+
- "Unable To Connect To Payment Provider"
|
|
153
|
+
- "Please Enter A Valid Email Address"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Why Sentence Case?
|
|
157
|
+
|
|
158
|
+
More readable, conversational, and modern. Better for accessibility and internationalization.
|
|
159
|
+
|
|
160
|
+
## Quick Reference
|
|
161
|
+
|
|
162
|
+
| Element Type | Example |
|
|
163
|
+
|--------------|---------|
|
|
164
|
+
| Button | "Save changes" |
|
|
165
|
+
| Link | "View all orders" |
|
|
166
|
+
| Label | "Email address" |
|
|
167
|
+
| Heading | "Payment options" |
|
|
168
|
+
| Message | "Order created successfully" |
|
|
169
|
+
| Menu item | "Product settings" |
|
|
170
|
+
| Checkbox | "Enable notifications" |
|
|
171
|
+
| Tab | "General settings" |
|
|
172
|
+
|
|
173
|
+
## When in Doubt
|
|
174
|
+
|
|
175
|
+
Capitalize only: first word, proper nouns, and acronyms.
|
|
176
|
+
|
|
177
|
+
Example: "Enable WooPayments API integration"
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Type Annotations for Static Analysis
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Overview](#overview)
|
|
6
|
+
- [When to Use PHPStan Annotations](#when-to-use-phpstan-annotations)
|
|
7
|
+
- [Generic Types with @template](#generic-types-with-template)
|
|
8
|
+
- [PHPStan-Specific Annotations](#phpstan-specific-annotations)
|
|
9
|
+
- [Common Patterns](#common-patterns)
|
|
10
|
+
- [Suppressing False Positives](#suppressing-false-positives)
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
WooCommerce uses PHPStan for static analysis. Beyond standard PHPDoc annotations (`@param`, `@return`, `@var`), use PHPStan-specific annotations to provide richer type information that enables better type inference.
|
|
15
|
+
|
|
16
|
+
## When to Use PHPStan Annotations
|
|
17
|
+
|
|
18
|
+
Use PHPStan annotations when:
|
|
19
|
+
|
|
20
|
+
- A method returns a type based on its input (generic/template types)
|
|
21
|
+
- Standard PHPDoc cannot express the type relationship
|
|
22
|
+
- You need to provide type information that PHP's type system cannot express
|
|
23
|
+
|
|
24
|
+
## Generic Types with @template
|
|
25
|
+
|
|
26
|
+
Use `@template` to declare generic type parameters. This enables PHPStan to infer return types based on input types.
|
|
27
|
+
|
|
28
|
+
### Basic Pattern
|
|
29
|
+
|
|
30
|
+
```php
|
|
31
|
+
/**
|
|
32
|
+
* Get an instance of a class from the container.
|
|
33
|
+
*
|
|
34
|
+
* @template T of object
|
|
35
|
+
* @param string $class_name Class name.
|
|
36
|
+
* @phpstan-param class-string<T> $class_name
|
|
37
|
+
*
|
|
38
|
+
* @return T The instance of the requested class.
|
|
39
|
+
*/
|
|
40
|
+
public function get( string $class_name ) {
|
|
41
|
+
// ...
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**How it works:**
|
|
46
|
+
|
|
47
|
+
1. `@template T of object` - Declares a type variable `T` constrained to objects
|
|
48
|
+
2. `@phpstan-param class-string<T> $class_name` - The input is a class name string for type `T`
|
|
49
|
+
3. `@return T` - The return type is the same `T` that was passed in
|
|
50
|
+
|
|
51
|
+
**Result:** PHPStan knows that `$container->get( MyService::class )` returns `MyService`.
|
|
52
|
+
|
|
53
|
+
### Constraint Options
|
|
54
|
+
|
|
55
|
+
```php
|
|
56
|
+
// Any object type
|
|
57
|
+
@template T of object
|
|
58
|
+
|
|
59
|
+
// Specific base class or interface
|
|
60
|
+
@template T of WC_Product
|
|
61
|
+
|
|
62
|
+
// No constraint (can be any type including scalars)
|
|
63
|
+
@template T
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## PHPStan-Specific Annotations
|
|
67
|
+
|
|
68
|
+
### @phpstan-param vs @param
|
|
69
|
+
|
|
70
|
+
Use both when you need PHPStan-specific type info while keeping standard documentation:
|
|
71
|
+
|
|
72
|
+
```php
|
|
73
|
+
/**
|
|
74
|
+
* @param string $class_name Class name to instantiate.
|
|
75
|
+
* @phpstan-param class-string<T> $class_name
|
|
76
|
+
*/
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- `@param string` - Standard PHPDoc (for IDEs and documentation generators)
|
|
80
|
+
- `@phpstan-param class-string<T>` - PHPStan-specific (richer type info)
|
|
81
|
+
|
|
82
|
+
### @phpstan-return
|
|
83
|
+
|
|
84
|
+
Use when the return type is more specific than the declared type:
|
|
85
|
+
|
|
86
|
+
```php
|
|
87
|
+
/**
|
|
88
|
+
* @return object
|
|
89
|
+
* @phpstan-return T
|
|
90
|
+
*/
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### @phpstan-var
|
|
94
|
+
|
|
95
|
+
Use for inline type assertions:
|
|
96
|
+
|
|
97
|
+
```php
|
|
98
|
+
/** @phpstan-var array<string, WC_Product> $products */
|
|
99
|
+
$products = get_transient( 'cached_products' );
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Common Patterns
|
|
103
|
+
|
|
104
|
+
### Factory Methods
|
|
105
|
+
|
|
106
|
+
```php
|
|
107
|
+
/**
|
|
108
|
+
* Create a new instance of a data store.
|
|
109
|
+
*
|
|
110
|
+
* @template T of WC_Data_Store
|
|
111
|
+
* @param string $object_type Object type (e.g., 'product', 'order').
|
|
112
|
+
* @phpstan-param class-string<T> $object_type
|
|
113
|
+
*
|
|
114
|
+
* @return T The data store instance.
|
|
115
|
+
*/
|
|
116
|
+
public static function load( string $object_type ) {
|
|
117
|
+
// ...
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Container/Service Locator
|
|
122
|
+
|
|
123
|
+
```php
|
|
124
|
+
/**
|
|
125
|
+
* @template T of object
|
|
126
|
+
* @param string $id Service identifier.
|
|
127
|
+
* @phpstan-param class-string<T> $id
|
|
128
|
+
*
|
|
129
|
+
* @return T Service instance.
|
|
130
|
+
*/
|
|
131
|
+
public function get( string $id );
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Collections with Known Types
|
|
135
|
+
|
|
136
|
+
```php
|
|
137
|
+
/**
|
|
138
|
+
* @param array<int, WC_Order_Item> $items Order items.
|
|
139
|
+
* @return array<string, float> Item totals keyed by item type.
|
|
140
|
+
*/
|
|
141
|
+
public function calculate_totals( array $items ): array {
|
|
142
|
+
// ...
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Suppressing False Positives
|
|
147
|
+
|
|
148
|
+
When PHPStan reports an error that is a false positive (the code is correct but PHPStan cannot verify it), use inline ignores with explanations:
|
|
149
|
+
|
|
150
|
+
```php
|
|
151
|
+
// @phpstan-ignore return.type (method uses reflection to return correct type at runtime)
|
|
152
|
+
return $this->create_instance( $class_name );
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Common ignore identifiers:
|
|
156
|
+
|
|
157
|
+
- `return.type` - Return type mismatch
|
|
158
|
+
- `argument.type` - Argument type mismatch
|
|
159
|
+
- `method.nonObject` - Method call on potentially non-object
|
|
160
|
+
|
|
161
|
+
**Important:** Only use ignores when the code is genuinely correct. Prefer fixing the type annotations or code when possible.
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# Unit Testing Conventions
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Complete Test File Template](#complete-test-file-template)
|
|
6
|
+
- [Test File Naming and Location](#test-file-naming-and-location)
|
|
7
|
+
- [System Under Test Variable](#system-under-test-variable)
|
|
8
|
+
- [Test Method Documentation](#test-method-documentation)
|
|
9
|
+
- [Comments in Tests](#comments-in-tests)
|
|
10
|
+
- [Test Configuration](#test-configuration)
|
|
11
|
+
- [Example: Payment Extension Suggestions Tests](#example-payment-extension-suggestions-tests)
|
|
12
|
+
- [Mocking the WooCommerce Logger](#mocking-the-woocommerce-logger)
|
|
13
|
+
- [General Testing Best Practices](#general-testing-best-practices)
|
|
14
|
+
|
|
15
|
+
## Complete Test File Template
|
|
16
|
+
|
|
17
|
+
Use this template when creating new test files. It shows all conventions applied together:
|
|
18
|
+
|
|
19
|
+
```php
|
|
20
|
+
<?php
|
|
21
|
+
declare( strict_types = 1 );
|
|
22
|
+
|
|
23
|
+
namespace Automattic\WooCommerce\Tests\Internal\Admin;
|
|
24
|
+
|
|
25
|
+
use Automattic\WooCommerce\Internal\Admin\OrderProcessor;
|
|
26
|
+
use WC_Unit_Test_Case;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Tests for the OrderProcessor class.
|
|
30
|
+
*/
|
|
31
|
+
class OrderProcessorTest extends WC_Unit_Test_Case {
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The System Under Test.
|
|
35
|
+
*
|
|
36
|
+
* @var OrderProcessor
|
|
37
|
+
*/
|
|
38
|
+
private $sut;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Set up test fixtures.
|
|
42
|
+
*/
|
|
43
|
+
public function setUp(): void {
|
|
44
|
+
parent::setUp();
|
|
45
|
+
$this->sut = new OrderProcessor();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Tear down test fixtures.
|
|
50
|
+
*/
|
|
51
|
+
public function tearDown(): void {
|
|
52
|
+
parent::tearDown();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @testdox Should return true when order is valid.
|
|
57
|
+
*/
|
|
58
|
+
public function test_returns_true_for_valid_order(): void {
|
|
59
|
+
$order = wc_create_order();
|
|
60
|
+
|
|
61
|
+
$result = $this->sut->is_valid( $order );
|
|
62
|
+
|
|
63
|
+
$this->assertTrue( $result, 'Valid orders should return true' );
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @testdox Should throw exception when order ID is negative.
|
|
68
|
+
*/
|
|
69
|
+
public function test_throws_exception_for_negative_order_id(): void {
|
|
70
|
+
$this->expectException( \InvalidArgumentException::class );
|
|
71
|
+
|
|
72
|
+
$this->sut->process( -1 );
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Key Elements
|
|
78
|
+
|
|
79
|
+
| Element | Requirement |
|
|
80
|
+
| ------- | ----------- |
|
|
81
|
+
| `declare( strict_types = 1 )` | Required at file start |
|
|
82
|
+
| Namespace | Match source location: `Automattic\WooCommerce\Tests\{path}` |
|
|
83
|
+
| Base class | Extend `WC_Unit_Test_Case` |
|
|
84
|
+
| SUT variable | Use `$sut` with docblock "The System Under Test." |
|
|
85
|
+
| Test docblock | Use `@testdox` with sentence ending in `.` |
|
|
86
|
+
| Return type | Use `void` for test methods |
|
|
87
|
+
| Assertion messages | Include helpful context for failures |
|
|
88
|
+
|
|
89
|
+
## Test File Naming and Location
|
|
90
|
+
|
|
91
|
+
| Source | Test | Pattern |
|
|
92
|
+
| -------------------- | ---------------------------------------------------- | ------------------------ |
|
|
93
|
+
| `includes/` classes | `tests/php/includes/{path}/class-wc-{name}-test.php` | Add `-test` suffix |
|
|
94
|
+
| `src/` classes | `tests/php/src/{path}/{name}Test.php` | Append `Test` (no hyphen)|
|
|
95
|
+
|
|
96
|
+
Test class: Same name as source class + `_Test` or `Test` suffix, extends `WC_Unit_Test_Case`
|
|
97
|
+
|
|
98
|
+
## System Under Test Variable
|
|
99
|
+
|
|
100
|
+
Use `$sut` with docblock "The System Under Test."
|
|
101
|
+
|
|
102
|
+
```php
|
|
103
|
+
/**
|
|
104
|
+
* The System Under Test.
|
|
105
|
+
*
|
|
106
|
+
* @var OrderProcessor
|
|
107
|
+
*/
|
|
108
|
+
private $sut;
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Test Method Documentation
|
|
112
|
+
|
|
113
|
+
When adding or modifying a unit test method, the part of the docblock that describes the test must be prepended with `@testdox`. End the comment with `.` for compliance with linting rules.
|
|
114
|
+
|
|
115
|
+
**Example:**
|
|
116
|
+
|
|
117
|
+
```php
|
|
118
|
+
/**
|
|
119
|
+
* @testdox Should return true when order is valid.
|
|
120
|
+
*/
|
|
121
|
+
public function test_returns_true_for_valid_order() {
|
|
122
|
+
// ...
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @testdox Should throw exception when order ID is negative.
|
|
127
|
+
*/
|
|
128
|
+
public function test_throws_exception_for_negative_order_id() {
|
|
129
|
+
// ...
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Comments in Tests
|
|
134
|
+
|
|
135
|
+
**Avoid over-commenting tests.** Test names and assertion messages should explain intent.
|
|
136
|
+
|
|
137
|
+
**Good - Self-explanatory:**
|
|
138
|
+
|
|
139
|
+
```php
|
|
140
|
+
/**
|
|
141
|
+
* @testdox Should return true when order status is draft.
|
|
142
|
+
*/
|
|
143
|
+
public function test_returns_true_for_draft_orders() {
|
|
144
|
+
$order = $this->create_draft_order();
|
|
145
|
+
|
|
146
|
+
$result = $this->sut->can_delete( $order );
|
|
147
|
+
|
|
148
|
+
$this->assertTrue( $result, 'Draft orders should be deletable' );
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Avoid - Over-commented:**
|
|
153
|
+
|
|
154
|
+
```php
|
|
155
|
+
/**
|
|
156
|
+
* @testdox Should return true when order status is draft.
|
|
157
|
+
*/
|
|
158
|
+
public function test_returns_true_for_draft_orders() {
|
|
159
|
+
// Create a draft order
|
|
160
|
+
$order = $this->create_draft_order();
|
|
161
|
+
|
|
162
|
+
// Call the method we're testing
|
|
163
|
+
$result = $this->sut->can_delete( $order );
|
|
164
|
+
|
|
165
|
+
// Verify the result is true
|
|
166
|
+
$this->assertTrue( $result, 'Draft orders should be deletable' );
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Avoid - Arrange/Act/Assert comments:**
|
|
171
|
+
|
|
172
|
+
```php
|
|
173
|
+
// Don't add these structural comments
|
|
174
|
+
// Arrange
|
|
175
|
+
$order = $this->create_draft_order();
|
|
176
|
+
|
|
177
|
+
// Act
|
|
178
|
+
$result = $this->sut->can_delete( $order );
|
|
179
|
+
|
|
180
|
+
// Assert
|
|
181
|
+
$this->assertTrue( $result );
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Use blank lines for visual separation instead. The test structure should be self-evident.
|
|
185
|
+
|
|
186
|
+
**When comments ARE useful in tests:**
|
|
187
|
+
|
|
188
|
+
- Explaining complex test setup: `// Simulate race condition by...`
|
|
189
|
+
- Documenting known issues: `// Workaround for WordPress core bug #12345`
|
|
190
|
+
- Clarifying business rules: `// Payment processor requires 24h hold`
|
|
191
|
+
|
|
192
|
+
## Test Configuration
|
|
193
|
+
|
|
194
|
+
Test configuration file: `phpunit.xml`
|
|
195
|
+
|
|
196
|
+
## Example: Payment Extension Suggestions Tests
|
|
197
|
+
|
|
198
|
+
The `PaymentsExtensionSuggestionsTest` class demonstrates good testing practices for country-specific functionality.
|
|
199
|
+
|
|
200
|
+
### Key Patterns Used
|
|
201
|
+
|
|
202
|
+
1. **Data-driven tests** using PHPUnit data providers
|
|
203
|
+
2. **Extension count verification** for different merchant types
|
|
204
|
+
3. **Clear test organization** by merchant type (online/offline)
|
|
205
|
+
|
|
206
|
+
### Example Test Structure
|
|
207
|
+
|
|
208
|
+
```php
|
|
209
|
+
class PaymentsExtensionSuggestionsTest extends WC_Unit_Test_Case {
|
|
210
|
+
/**
|
|
211
|
+
* The System Under Test.
|
|
212
|
+
*
|
|
213
|
+
* @var PaymentsExtensionSuggestions
|
|
214
|
+
*/
|
|
215
|
+
private $sut;
|
|
216
|
+
|
|
217
|
+
public function setUp(): void {
|
|
218
|
+
parent::setUp();
|
|
219
|
+
$this->sut = new PaymentsExtensionSuggestions();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* @testdox Should return correct extension count for online merchants by country
|
|
224
|
+
* @dataProvider online_merchant_country_data
|
|
225
|
+
*/
|
|
226
|
+
public function test_get_country_extensions_count_for_online_merchants(
|
|
227
|
+
string $country_code,
|
|
228
|
+
int $expected_count
|
|
229
|
+
) {
|
|
230
|
+
$merchant = array(
|
|
231
|
+
'country' => $country_code,
|
|
232
|
+
'selling_venues' => 'online',
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
$result = $this->sut->get_country_extensions_count( $merchant );
|
|
236
|
+
|
|
237
|
+
$this->assertSame(
|
|
238
|
+
$expected_count,
|
|
239
|
+
$result,
|
|
240
|
+
"Expected {$expected_count} extensions for online merchant in {$country_code}"
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Data provider for online merchant tests.
|
|
246
|
+
*
|
|
247
|
+
* @return array
|
|
248
|
+
*/
|
|
249
|
+
public function online_merchant_country_data() {
|
|
250
|
+
return array(
|
|
251
|
+
'United States' => array( 'US', 5 ),
|
|
252
|
+
'United Kingdom' => array( 'GB', 4 ),
|
|
253
|
+
'Canada' => array( 'CA', 3 ),
|
|
254
|
+
'Australia' => array( 'AU', 3 ),
|
|
255
|
+
// ... more countries
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @testdox Should return correct extension count for offline merchants by country
|
|
261
|
+
* @dataProvider offline_merchant_country_data
|
|
262
|
+
*/
|
|
263
|
+
public function test_get_country_extensions_count_for_offline_merchants(
|
|
264
|
+
string $country_code,
|
|
265
|
+
int $expected_count
|
|
266
|
+
) {
|
|
267
|
+
$merchant = array(
|
|
268
|
+
'country' => $country_code,
|
|
269
|
+
'selling_venues' => 'offline',
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
$result = $this->sut->get_country_extensions_count( $merchant );
|
|
273
|
+
|
|
274
|
+
$this->assertSame(
|
|
275
|
+
$expected_count,
|
|
276
|
+
$result,
|
|
277
|
+
"Expected {$expected_count} extensions for offline merchant in {$country_code}"
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Data provider for offline merchant tests.
|
|
283
|
+
*
|
|
284
|
+
* @return array
|
|
285
|
+
*/
|
|
286
|
+
public function offline_merchant_country_data() {
|
|
287
|
+
return array(
|
|
288
|
+
'United States' => array( 'US', 2 ),
|
|
289
|
+
'United Kingdom' => array( 'GB', 1 ),
|
|
290
|
+
'Canada' => array( 'CA', 1 ),
|
|
291
|
+
// ... more countries
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Important Notes for Payment Extension Tests
|
|
298
|
+
|
|
299
|
+
When working with payment extension suggestions:
|
|
300
|
+
|
|
301
|
+
1. **Extension counts must match the implementation** in `src/Internal/Admin/Suggestions/PaymentsExtensionSuggestions.php`
|
|
302
|
+
2. **When adding new countries** to the implementation, update both data providers in the test file
|
|
303
|
+
3. **Tests are separated by merchant type** (online vs offline) as they have different extension counts
|
|
304
|
+
4. **Data providers use descriptive keys** (country names) for better test output
|
|
305
|
+
|
|
306
|
+
## Mocking the WooCommerce Logger
|
|
307
|
+
|
|
308
|
+
When testing code that uses `wc_get_logger()` (directly or via `SafeGlobalFunctionProxy::wc_get_logger()`), use the `woocommerce_logging_class` filter to inject a fake logger.
|
|
309
|
+
|
|
310
|
+
### Why the Filter Approach?
|
|
311
|
+
|
|
312
|
+
- `register_legacy_proxy_function_mocks` doesn't intercept `SafeGlobalFunctionProxy` calls
|
|
313
|
+
- Passing an object (not a class name string) bypasses `wc_get_logger()`'s internal cache
|
|
314
|
+
|
|
315
|
+
### Creating a Fake Logger
|
|
316
|
+
|
|
317
|
+
The fake logger must implement `WC_Logger_Interface`. Create an anonymous class with public arrays to track calls (`$debug_calls`, `$warning_calls`, etc.) and implement all interface methods (`add`, `log`, `debug`, `info`, `warning`, `error`, `emergency`, `alert`, `critical`, `notice`).
|
|
318
|
+
|
|
319
|
+
### Using the Fake Logger
|
|
320
|
+
|
|
321
|
+
```php
|
|
322
|
+
public function test_logs_warning_for_invalid_input(): void {
|
|
323
|
+
$fake_logger = $this->create_fake_logger();
|
|
324
|
+
|
|
325
|
+
// Inject via filter - passing object bypasses cache.
|
|
326
|
+
add_filter(
|
|
327
|
+
'woocommerce_logging_class',
|
|
328
|
+
function () use ( $fake_logger ) {
|
|
329
|
+
return $fake_logger;
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
$this->sut->process_input( 'invalid-value' );
|
|
334
|
+
|
|
335
|
+
$this->assertCount( 1, $fake_logger->warning_calls );
|
|
336
|
+
|
|
337
|
+
remove_all_filters( 'woocommerce_logging_class' ); // Always clean up.
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Key Points
|
|
342
|
+
|
|
343
|
+
| Aspect | Detail |
|
|
344
|
+
| ------------ | ----------------------------------------------- |
|
|
345
|
+
| Filter name | `woocommerce_logging_class` |
|
|
346
|
+
| Return value | Object instance (not class name string) |
|
|
347
|
+
| Interface | Must implement `WC_Logger_Interface` |
|
|
348
|
+
| Cleanup | Always call `remove_all_filters()` after test |
|
|
349
|
+
|
|
350
|
+
### Reference
|
|
351
|
+
|
|
352
|
+
See `PaymentGatewayTest.php:create_fake_logger()` for a complete implementation.
|
|
353
|
+
|
|
354
|
+
## General Testing Best Practices
|
|
355
|
+
|
|
356
|
+
1. **Always run tests after making changes** to verify functionality
|
|
357
|
+
2. **Use specific test filters** during development (see running-tests.md in the woocommerce-dev-cycle skill)
|
|
358
|
+
3. **Write descriptive test names** that explain what is being tested
|
|
359
|
+
4. **Use data providers** for testing multiple scenarios with the same logic
|
|
360
|
+
5. **Include helpful assertion messages** for debugging when tests fail
|
|
361
|
+
6. **Test both success and failure cases**
|
|
362
|
+
7. **Mock external dependencies** (database, API calls, etc.)
|