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
25 changes: 24 additions & 1 deletion .github/workflows/jest-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,27 @@ jobs:
run: npm ci --ignore-scripts

- name: Check formatting
run: npx prettier --check packages/join-flow
run: npx prettier --check packages/join-flow packages/join-e2e

typecheck:
name: Type check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: '20'

# TypeScript, plus each package's dependencies. The root postinstall
# also runs Composer, which the type check doesn't need, so it's
# skipped and join-flow's dependencies are installed directly.
- name: Install dependencies
run: |
npm ci --ignore-scripts
npm --prefix packages/join-flow ci
npm --prefix packages/join-e2e ci

# The join form and its Jest tests, the E2E tests and the fake API.
- name: Type check
run: npm run typecheck
4 changes: 3 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
packages/join-flow/build
packages/join-flow/dist
packages/join-flow/dist
packages/join-e2e/test-results
packages/join-e2e/playwright-report
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"build": "npm --prefix packages/join-flow run build",
"dist": "npm run build && scripts/package.sh",
"deploy": "npm run build && sh scripts/deploy.sh",
"prettier": "prettier --write packages/join-flow"
"prettier": "prettier --write packages/join-flow packages/join-e2e",
"typecheck": "tsc --noEmit -p packages/join-flow && npm --prefix packages/join-e2e run typecheck"
Comment thread
conatus marked this conversation as resolved.
Resolved
},
"devDependencies": {
"prettier": "^3.9.10",
Expand Down
7 changes: 6 additions & 1 deletion packages/join-block/phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

<file>.</file>
<exclude-pattern>vendor/*</exclude-pattern>
<exclude-pattern>tests/*</exclude-pattern>
<exclude-pattern>wordpress/*</exclude-pattern>
<arg name="extensions" value="php"/>

Expand All @@ -18,6 +17,12 @@
<exclude name="PSR1.Files.SideEffects.FoundWithSymbols"/>
</rule>

<!-- Test doubles mirror WordPress's snake_case APIs ($wpdb->get_results
and the like), and some tests use PHPUnit's test_snake_case names. -->
<rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
<exclude-pattern>tests/*</exclude-pattern>
Comment thread
conatus marked this conversation as resolved.
Resolved
</rule>

<!-- CK_Theme_Options_Container extends Carbon Fields' snake_case API:
to_json() overrides the parent method, so PSR12 naming cannot apply. -->
<rule ref="Squiz.Classes.ValidClassName.NotCamelCaps">
Expand Down
1 change: 1 addition & 0 deletions packages/join-block/phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ parameters:
paths:
- src
- join.php
- tests
scanDirectories:
- vendor
- wordpress
Expand Down
11 changes: 6 additions & 5 deletions packages/join-block/tests/JoinServiceFreeMembershipTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,14 @@ public function get_results(string $query, $output = null): array
$joinBlockLog = new class {
public array $errors = [];

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function info(string $msg, array $ctx = []): void {}
public function info(string $msg, array $ctx = []): void
{
}

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function warning(string $msg, array $ctx = []): void {}
public function warning(string $msg, array $ctx = []): void
{
}

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function error(string $msg, array $ctx = []): void
{
$this->errors[] = $msg;
Expand Down
11 changes: 6 additions & 5 deletions packages/join-block/tests/JoinServiceMailchimpTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,14 @@ public function get_results(string $query, $output = null): array
$joinBlockLog = new class {
public array $errors = [];

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function info(string $msg, array $ctx = []): void {}
public function info(string $msg, array $ctx = []): void
{
}

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function warning(string $msg, array $ctx = []): void {}
public function warning(string $msg, array $ctx = []): void
{
}

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function error(string $msg, array $ctx = []): void
{
$this->errors[] = $msg;
Expand Down
3 changes: 0 additions & 3 deletions packages/join-block/tests/LapsingFilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,12 @@ protected function setUp(): void

global $joinBlockLog;
$joinBlockLog = new class {
// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function info($msg, $ctx = [])
{
}
// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function warning($msg, $ctx = [])
{
}
// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function error($msg, $ctx = [])
{
}
Expand Down
3 changes: 0 additions & 3 deletions packages/join-block/tests/MailchimpServiceLegacyTagsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,16 @@ protected function setUp(): void
public array $warnings = [];
public array $errors = [];

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function info(string $msg, array $ctx = []): void
{
$this->infos[] = $msg;
}

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function warning(string $msg, array $ctx = []): void
{
$this->warnings[] = $msg;
}

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function error(string $msg, array $ctx = []): void
{
$this->errors[] = $msg;
Expand Down
11 changes: 6 additions & 5 deletions packages/join-block/tests/MailchimpServiceTagsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ protected function setUp(): void
$joinBlockLog = new class {
public array $errors = [];

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function info(string $msg, array $ctx = []): void {}
public function info(string $msg, array $ctx = []): void
{
}

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function warning(string $msg, array $ctx = []): void {}
public function warning(string $msg, array $ctx = []): void
{
}

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function error(string $msg, array $ctx = []): void
{
$this->errors[] = $msg;
Expand Down
4 changes: 2 additions & 2 deletions packages/join-block/tests/SessionLockTestProcess.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
require_once(__DIR__ . "/../vendor/autoload.php");

// Load WordPress functions required by JoinService::acquireLock()
define( 'ABSPATH', __DIR__ . '/../wordpress/' );
define( 'WPINC', 'wp-includes' );
define('ABSPATH', __DIR__ . '/../wordpress/');
define('WPINC', 'wp-includes');
require_once(__DIR__ . "/../wordpress/wp-includes/formatting.php");
require_once(__DIR__ . "/../wordpress/wp-includes/functions.php");

Expand Down
5 changes: 0 additions & 5 deletions packages/join-block/tests/SettingsComputeTagsToRemoveTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ class SettingsComputeTagsToRemoveTest extends TestCase
/** Values returned by the carbon_get_theme_option stub, keyed by field name. */
private array $settings = [];

/** Plan rows returned by the $wpdb stub, as stored in wp_options. */
private array $storedPlans = [];

protected function setUp(): void
{
parent::setUp();
Expand All @@ -49,12 +46,10 @@ public function prepare(string $query, ...$args): string
{
return $query;
}
// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function esc_like(string $text): string
{
return $text;
}
// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function get_results(string $query, $output = null): array
{
return $this->rows;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ public function testReturnsFalsyWhenNoStoredPlanMatches(): void
private function stubWpdbWithRows(array $rows): void
{
global $wpdb;
$wpdb = Mockery::mock();
/** @var \wpdb&\Mockery\MockInterface $wpdb */
Comment thread
conatus marked this conversation as resolved.
Resolved
$wpdb = Mockery::mock(\wpdb::class);
$wpdb->options = 'wp_options';
$wpdb->shouldReceive('esc_like')->andReturnUsing(fn($s) => $s);
$wpdb->shouldReceive('prepare')->andReturnUsing(fn($sql) => $sql);
Expand Down
14 changes: 8 additions & 6 deletions packages/join-block/tests/StripeInvoicePaidTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ protected function setUp(): void
$joinBlockLog = new class {
public array $errors = [];

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function info(string $msg, array $ctx = []): void {}
public function info(string $msg, array $ctx = []): void
{
}

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function warning(string $msg, array $ctx = []): void {}
public function warning(string $msg, array $ctx = []): void
{
}

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function error(string $msg, array $ctx = []): void
{
$this->errors[] = $msg;
Expand All @@ -52,7 +53,8 @@ protected function tearDown(): void
{
global $joinBlockLog;
$joinBlockLog = null;
\Stripe\ApiRequestor::setHttpClient(null);
// Back to the client Stripe uses by default.
\Stripe\ApiRequestor::setHttpClient(\Stripe\HttpClient\CurlClient::instance());
Monkey\tearDown();
parent::tearDown();
}
Expand Down
4 changes: 2 additions & 2 deletions packages/join-block/tests/StripeWebhookTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@ public function testResolveTierTagChangesSharedTagsAreNotRemoved(): void
$result = $this->callResolveTierTagChanges($newPlan, $oldPlan);

$this->assertContains('high-wage', $result['addTags']);
$this->assertContains('member', $result['addTags']);
$this->assertContains('low-wage', $result['removeTags']);
$this->assertContains('member', $result['addTags']);
$this->assertContains('low-wage', $result['removeTags']);
$this->assertNotContains('member', $result['removeTags'], 'member must not be removed when it is also being added');
}

Expand Down
3 changes: 0 additions & 3 deletions packages/join-block/tests/ZetkinServiceTagsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,16 @@ protected function setUp(): void
public array $warnings = [];
public array $errors = [];

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function info(string $msg, array $ctx = []): void
{
$this->infos[] = $msg;
}

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function warning(string $msg, array $ctx = []): void
{
$this->warnings[] = $msg;
}

// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function error(string $msg, array $ctx = []): void
{
$this->errors[] = $msg;
Expand Down
5 changes: 5 additions & 0 deletions packages/join-e2e/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"singleQuote": true,
"printWidth": 120,
"trailingComma": "all"
}
37 changes: 31 additions & 6 deletions packages/join-e2e/fake-api/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function freshStripe() {
return { counter: 0, objects: { products: [], prices: [], customers: [], subscriptions: [], invoices: [] } };
}

/** @returns {{ counter: number, tags: { id: number, title: string }[] }} */
function freshZetkin() {
return { counter: 0, tags: [] };
}
Expand Down Expand Up @@ -115,18 +116,29 @@ function fakeStripe(method, url, body) {
return { status: 200, body: { object: 'search_result', url: url.pathname, has_more: false, data } };
}
if (method === 'GET' && !idOrAction) {
return { status: 200, body: { object: 'list', url: url.pathname, has_more: false, data: listStripe(type, store, url) } };
return {
status: 200,
body: { object: 'list', url: url.pathname, has_more: false, data: listStripe(type, store, url) },
};
}
if (method === 'POST' && !idOrAction) {
stripe.counter += 1;
const object = { id: `${prefix}_e2e_${stripe.counter}`, object: type.replace(/s$/, ''), active: true, livemode: false, ...body };
const object = {
id: `${prefix}_e2e_${stripe.counter}`,
object: type.replace(/s$/, ''),
active: true,
livemode: false,
...body,
};
if (body && body.unit_amount) object.unit_amount = Number(body.unit_amount);
store.push(object);
return { status: 200, body: object };
}
if (method === 'GET' && idOrAction) {
const object = store.find((candidate) => candidate.id === idOrAction);
return object ? { status: 200, body: object } : { status: 404, body: { error: { type: 'invalid_request_error', message: 'No such object' } } };
return object
? { status: 200, body: object }
: { status: 404, body: { error: { type: 'invalid_request_error', message: 'No such object' } } };
}
return null;
}
Expand Down Expand Up @@ -180,12 +192,17 @@ function respond(host, method, url, body) {
return { status: 200, body: {} };
}

/**
* @param {import('http').IncomingMessage} request
* @param {import('http').ServerResponse} response
*/
function handleService(request, response) {
/** @type {Buffer[]} */
const chunks = [];
request.on('data', (chunk) => chunks.push(chunk));
request.on('end', () => {
const host = (request.headers.host || '').split(':')[0];
const url = new URL(request.url, `https://${host}`);
const url = new URL(request.url || '/', `https://${host}`);
let buffer = Buffer.concat(chunks);
// Sentry, for one, gzips what it sends. A body that won't unzip is
// recorded as it came, rather than crashing the fake.
Expand Down Expand Up @@ -213,7 +230,12 @@ function handleService(request, response) {
});
}

/**
* @param {import('http').IncomingMessage} request
* @param {import('http').ServerResponse} response
*/
function handleControl(request, response) {
/** @type {Buffer[]} */
const chunks = [];
request.on('data', (chunk) => chunks.push(chunk));
request.on('end', () => {
Expand Down Expand Up @@ -252,7 +274,7 @@ function handleControl(request, response) {
}
// Puts an object straight into the fake Stripe, e.g. POST /stripe/customers
// with {"id": "cus_1", "email": "..."}, for tests that need one to exist.
const stripeSeed = request.url.match(/^\/stripe\/([a-z_]+)$/);
const stripeSeed = (request.url || '').match(/^\/stripe\/([a-z_]+)$/);
if (request.method === 'POST' && stripeSeed && stripe.objects[stripeSeed[1]]) {
const object = JSON.parse(Buffer.concat(chunks).toString('utf8'));
stripe.objects[stripeSeed[1]].push(object);
Expand All @@ -265,7 +287,10 @@ function handleControl(request, response) {

const certs = path.join(__dirname, 'certs');
https
.createServer({ key: fs.readFileSync(path.join(certs, 'server.key')), cert: fs.readFileSync(path.join(certs, 'server.crt')) }, handleService)
.createServer(
{ key: fs.readFileSync(path.join(certs, 'server.key')), cert: fs.readFileSync(path.join(certs, 'server.crt')) },
handleService,
)
.listen(443);
http.createServer(handleService).listen(80);
http.createServer(handleControl).listen(8080);
Expand Down
10 changes: 10 additions & 0 deletions packages/join-e2e/fake-api/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"noImplicitAny": false,
"useUnknownInCatchVariables": false
},
"include": ["."]
}
Loading
Loading