Categories
Funemployed Software Development

Laravel Url Shortener FTW

Where I discuss creating a bitly style self-hosted URL shortener written for the Laravel framework.

While working through my funemployment (which wasn’t fun, but maybe I can convince myself) I started a side-project to (re-)learn Laravel. The last time I used Laravel was version 4 or 5 — it’s now v. 12 so there’s been a few changes.

My need to learn combined with my need to be more frugal lately (💸 no paychecks). I’ve been brainstorming methods limiting websites dependencies on databases (which typically incur an increased cost from additional hardware requirements). And given one of my websites lost it’s hosting and used to have YOURLS deployed on it I wanted an alternative I could play around with.

Sidenote: a URL shortener essentially allows a mapping from a site you own (e.g. n3f.co) to a much larger or longer URL. There really isn’t much use for it, but I enjoy the vanity aspect (controlling my own), making short urls as bookmarks, sharing links, and tracking clicks.

The actual implementation was pretty simple. Laravel made creating the routes really easy and I just had to create a couple react components and add them to the default project. I thought this might be a fun project to try with Rails and Go as well if I ever get the time (and I wouldn’t even have to do much to the frontend).

Route::controller(UrlController::class)->group(function () {
    Route::post('urls', 'store')->name('urls.store');
    Route::patch('urls/{id}', 'edit')->name('urls.edit');
    Route::get('urls/{id}/stats', 'stats')->name('urls.stats');
    Route::delete('urls/{id}', 'destroy')->name('urls.destroy');
})->middleware(['auth', 'verified', 'throttle:10,1']);
Code language: PHP (php)
Url Shortener edit form
Url Shortener edit form
set an expiration

Right now the feature set isn’t comprehensive, but you can currently:

  • Create a short url
  • Define the alias or short code. (Or just use an apparently randomly generated one)
  • Set an expiration for a defined url (i.e. after a certain timestamp the redirection to the target will stop working)
  • Edit or delete your defined urls
  • Currently, I’m preserving all requests, but I need additional analytics processing and UI to surface results.
  • Uses any database Laravel supports including SQLite (which is free–this results in roughly 50% savings for me 🤑)

Features I need to add (or would like to):

  • search
  • stats (other than simple clicks)
  • QR code sharing.
  • bookmarklets (are these even used any more?)

I’m curious if anyone else would find this project helpful. Would you use a link shortener? If so, what features would you like to see?

Categories
Funemployed Software Development

Deploying Laravel application to nearlyfreespeech.net

I recently created a Laravel application as a side-project to re-learn the framework. (I’ll post more about it later. It’s a URL Shortener). The documentation for getting this application was mostly correct. However, I discovered at least one issue because I wanted to save money and use a SQLite database instead of the more costly MySQL one.

Transfer files

Get your files over to the site by rsync-ing the directory to the protected area:

# Replace $ACCOUNT with your NFSN account username and 
# $HOST with your assigned server hostname.
$ rsync -avz --exclude='.git' \
  --exclude='node_modules' \
  --exclude='storage/logs/*' \
  --exclude='storage/framework/cache/*' \
  --exclude='storage/framework/sessions/*' \
  --exclude='storage/framework/views/*' \
  --exclude='.env' --exclude='vendor' \
  --exclude='.DS_Store' --exclude='*.log' \
  . $ACCOUNT@$HOST:/home/protected/laravel-app/Code language: Bash (bash)

Update .env

Depending on your application this could be complicated or easy. Copy your .env.example to .env and make any changes necessary.

Set file system permissions

I found 3 directories I needed to update, if you aren’t using the default sqlite database you can skip that one.

#!/bin/bash

directories=(
    "database"        # ⚠️ where the sqlite database goes
    "storage"         # logs/cache
    "bootstrap/cache" # cache (duh)
)

for directory in "${directories[@]}"; do
    chown -R web "$directory"
    chmod -R 775 "$directory"
doneCode language: PHP (php)

Get public directory configured

The public directory is locked down so it can’t be moved or overwritten. I moved everything in my Laravel app’s public directory into the nearlyfreespeech one, deleted the empty directory, and then linked to the canonical one. Effectively it lets the public directory be in 2 places at the same time.

From inside the laravel-app directory:

$ ln<span style="background-color: initial; font-size: inherit; text-align: initial; text-wrap-mode: wrap; color: inherit; letter-spacing: -0.015em;"> -s ../../public public</span>Code language: Bash (bash)

Rebuild application

You can forego this step if you transfer your vendor directory and run npm run build first too. From the Laravel app directory:

  1. composer install
  2. npm install
  3. npm run build
  4. php artisan optimize

.htaccess

Either move or copy the provided .htaccess file to public. Laravel requires its own .htaccess rules for routing (otherwise all requests won’t be directed through index.php).

Conclusion

That should be it. Test your application and get it running. I found a problem with a models generated attributes–💡I had to add the $appends property–but in general it seems to be working well.

Categories
Funemployed Software Development

Display Open Graph data with Link Preview Card Block

A couple weeks ago, I started entertaining offers for employment. As part of that effort I spun up my old resume site, dusted off the cobwebs and updated the content. After it was all bright and shiny, I sent the link to my wife over text. She’s not an engineer but she is a fantastic writer. I was disappointed it didn’t generate a cool picture and excerpt as many links do these days. Why not?

I’ll share you the google (or AI conversation depending on your preferences), but I found the mechanism to create that content is provided by an “open graph protocol”. Really this “protocol” (by Meta™) is nothing more than adding a couple of lines to the website’s HTML. (There is also a competing protocol/standard used by Twitter/X). They’re both pretty easy to add.

After adding a couple of fields to my website, behold:

Apple doesn’t show a description; just picture, title and link.

If you’re interested in adding support for your own site, that’s not what this post is about 😆.

I believe this is a great way to link content. It not only shows what to expect from a link–a quick title and description–it also adds a large click target. When I went to create my post, I was disappointed I couldn’t find a plugin supporting this card data on WordPress sites. (It’s probably already there, but I figured it would be fun to roll my own rather than use someone else’s).

After a quick coding sesh, I threw together an early version. Now with the Link Preview Card plugin (wordpress.org), you too can add these cards to your posts and pages.

Result

I’m fond of the slight zoom when you hover over it.

Features:

  • Enter a link.
  • Caches site data (via an API call).
  • Displays open graph card (with slight zoom on hover)

For a general proof of concept this is definitely workable. I thought it was fun to play with. I used AI to evaluate better testing approaches to plugin development (still haven’t found a great method for doing this). I have a list of TODO’s that are still low hanging fruit, but would love any thoughts for new features.

If you have a site, give it a spin and kick the tires. Let me know how it goes.

Categories
Funemployed Software Development

Hard Working Engineer Available

I just shared this on Facebook and thought I should post here too: for those that don’t know on April 2, I was laid-off (from a job I loved; and with nearly 20% of the company).  I’ve been looking for a new job non-stop for the last 3 months, but this market is pretty brutal.  If you know of anyone looking for a senior or staff software engineer, let me know, or point them to 👇

Remote or SoCal would be preferred, but I’m open to anything.

Categories
Sabbatical

Sabbatical Phase 3 & 4

As part of my work with Automattic, I’m granted a 3 month sabbatical after 5 years and I want to document some of the projects and things that I manage to do.

Well, the sabbatical is drawing to a close. 2 weeks left.

Phase 3 (primarily Christmas)

  • Made treats for friends
  • Decorated the house (lights outside, garlands inside)
  • Spent time with family – didn’t get any surfing done really.
  • Many basketball games.
  • Updated nef.family site. See this post.
  • Christmas!
  • New Years!
  • Water heater broke. We attempted a repair under the warranty, but the replacement parts didn’t fix the issue. We were sick of not having hot water (we went over a week without it). So, we bought and installed a new one.

Phase 4

I’m currently in phase 4 and I’m trying to get a couple projects done before I return to work so we’ll see what I can accomplish.

  • 🛑Working on getting finlaysons.us site working with an autobiography of my great grandfather. (He was an inventor). I ran the printed copy I have through AWS OCR tool and started editing it for correctness. Status: on-hold, waiting for missing pages from my Mom.
  • ✅ Had first speaking assignment with my new church calling: 20 minutes of speaking took a few days to write and edit. This was more work than I expected.
  • ✅I have a bunch of CD’s that I needed to back up. Bought a new DVD/Blu-ray drive to rip them.
  • ✅ Backed up all family pictures to cloud.
  • 🛑 Fixing broken kindle: I bought a new screen for the version of Kindle I have (using the information from ifixit). This was incorrect information for the 10th generation kindle paperwhite 4, and the new screen is not the correct replacement. Seems that this kindle is borked. Not sure if I should recycle or try to sell on ebay.
  • ✅ Added recipes to family site.
StatCount
Books read5 (+4) Primarily padding to reach goal of 52 books for the year.
Video games played5 (+2) Death Stranding/Ghosts of Tsushima
Surf trips3 (+0)
Movies (in theater)4 (+2) Sonic 3, Mufasa

Categories
Software Development

Creating a Photo Album Taxonomy and Bulk Importing Images

I finally tackled a project I’ve had on my plate for quite a long time. Once upon a time, my wife blogged our family shenanigans. They are great memories and I love them. She (and I) spent a significant amount of time putting in some effort to upload and caption photos as well. Re-reading the stories of our young family and preserving those memories are priceless.

However, the software I used originally, fell out of use and eventually was no longer supported. It limped along for many years and our site fell into that abandoned ghost town territory common to older blogs. (It appears the software was dormant for 7 years and in 2019 someone started working it again – 🤔. But now I want to only have “one” solution…)

To preserve those posts I had a couple goals.

  1. Update the site to be as standard based as possible.
  2. Migrating a site to another system should be easy(-ish).
  3. Keep the posts the same as possible.

Originally, I thought about migrating to a new site. I decided to stay with WordPress, but the sticking part was migrating the photo albums. In order to support my goals, I made the decision to use the WordPress built-in media library and taxonomies.

Creating the taxonomy itself was pretty easy:

// register new taxonomy which applies to attachments
function at_add_album_taxonomy() {
	$labels = array(
		'name'              => __( 'Albums', 'album_taxonomy' ),
		'singular_name'     => __( 'Album', 'album_taxonomy' ),
		'search_items'      => __( 'Search Albums', 'album_taxonomy' ),
		'all_items'         => __( 'All Albums', 'album_taxonomy' ),
		'parent_item'       => __( 'Parent Album', 'album_taxonomy' ),
		'parent_item_colon' => __( 'Parent Album:', 'album_taxonomy' ),
		'edit_item'         => __( 'Edit Album', 'album_taxonomy' ),
		'update_item'       => __( 'Update Album', 'album_taxonomy' ),
		'add_new_item'      => __( 'Add New Album', 'album_taxonomy' ),
		'new_item_name'     => __( 'New Album Name', 'album_taxonomy' ),
		'menu_name'         => __( 'Albums', 'album_taxonomy' ),
	);
	$args = array(
		'public' => true,
		'labels' => $labels,
		'hierarchical' => true,
		'rewrite' => [
			'slug' => 'albums',
			'hierarchical' => true,
		],
		'show_admin_column' => 'true',
		'show_in_rest' => true,
		'update_count_callback' => '_update_generic_term_count',
	);
	register_taxonomy( 'albums', 'attachment', $args );
}
add_action( 'init', 'at_add_album_taxonomy', 0 );Code language: PHP (php)

Note: the biggest takeaways from this section were the rewrite and update_count_callback args. The rewrite hierarchies supports the archives page (which effectively is the album page) displaying a multi-tier URL for sub-albums.

The update_count_callback change makes the counter work the way you think it should (each album counts any image tagged appropriately).

Adding the images and a Bulk Import:

After exporting the images into a folder and backing up the database, I imported the database into a sqlite file. I don’t remember the steps I took to do that, but it must not have been super difficult 😆.

I originally created a python script running WP-CLI commands to import the images (with appropriate metadata). After playing around I realized the round-trip nature (send the command, execute, return) made it prohibitive for importing 6K images.

AI actually made quick work of converting that script into a WP-CLI command I would upload and run directly. I’m not generally enamored with AI for actual production software (yet) – but it was perfect for this one-off use.

Because of the specific nature of my import I’m not sure how useful the script would be to anyone else. In broad strokes, it takes a folder and sqlite db as input, iterates through each file, pulls metadata from file/db, imports file, and sets the image metadata and taxonomy.

When I was running locally it blazed through my folders in a number of seconds, but remotely it took some time. While it was running I noticed it slowing down so I added a throttle (sleep after running 200 images).

Other WordPress changes:

Finally I have the images uploaded and now I needed to make them searchable for making the changes. (Side note: I plan on going into each post and fine-tuning the posts by hand because I want to remove the custom HTML originally used and replace with WordPress block structuring.)

In addition to the album taxonomy I preserved the original “path” and “id” of the image. To add these terms to the media library search I did the following:

1. Create a search specific query var:

// Need to hook into media library search to filter by album or g2id (metadata)
function at_search_meta( $query ) {
	$query['at_search_meta'] = true;
	return $query;
}
add_filter( 'ajax_query_attachments_args', 'at_search_meta' );Code language: PHP (php)

2. Adjust the search parameters:

The posts_search function adjusts the WHERE clause by removing the end parenthesis, adds the columns I want to search with, and replaces the parenthesis.

The posts_clauses function adds the meta table as a separate alias (used in posts_search) because WordPress does a different join with the metadata table.

add_filter( 'posts_search', function( $search, $query ) {
	if ( ! filter_var( $query->get('at_search_meta'), FILTER_VALIDATE_BOOLEAN )
	     || empty( $query->get('s') )
	) {
		return $search;
	}

	// Remove the last ')' and add our own search clauses...
	$search = preg_replace( '/\)\s*$/', '', $search );
	$search .= " OR (at_meta.meta_key = 'g2id' AND nef_meta.meta_value LIKE '%" . $query->get('s') . "%')";
	$search .= " OR (at_meta.meta_key = 'g2path' AND nef_meta.meta_value LIKE '%" . $query->get('s') . "%')";
	$search .= ') ';
	return $search;
}, 100, 2 );

add_filter( 'posts_clauses', function( $clauses, $query ) {
	if ( ! filter_var( $query->get('at_search_meta'), FILTER_VALIDATE_BOOLEAN )
	     || empty( $query->get('s') )
	) {
		return $clauses;
	}

	global $wpdb;
	// Check if we need to join the postmeta table
	$clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} AS at_meta ON at_meta.post_id = {$wpdb->posts}.ID";
	$clauses['distinct'] = 'DISTINCT';

	return $clauses;
}, 100, 2 );Code language: PHP (php)

✨Add metadata to image metadata UI:

Not necessarily something anyone else would need, this snippet adds specific metadata just to the UI as a helper.

// Attachment details (show metadata)
function at_attachment_details( $form_fields, $post ) {
	$form_fields['g2id'] = array(
		'label' => 'G2 ID',
		'input' => 'text',
		'value' => get_post_meta( $post->ID, 'g2id', true ),
	);
	$form_fields['g2path'] = array(
		'label' => 'G2 Path',
		'input' => 'text',
		'value' => get_post_meta( $post->ID, 'g2path', true ),
	);
	return $form_fields;
}
add_filter( 'attachment_fields_to_edit', 'at_attachment_details', 10, 2 );Code language: PHP (php)

Displaying the lovely albums:

This was the part I wanted to document the most since it was done primarily “by hand” using the built-in site editor.

I used the create-block-theme plugin to export the current theme into a modifiable version I can adjust. Then clicked the “Add template” button (currently at the top-right when viewing templates), and chose the option for an Albums page. (This creates a taxonomy-albums.html file).

In the editor for this template I selected the “Post template” block and changed it to grid.

Additionally, I had to add this snippet to get the images to display because they weren’t attached to existing posts and had an inherit post status. YMMV depending on your import.

// Adjust archive query to include attachments (with "inherit" post_status).
add_filter( 'pre_get_posts', function( $query ) {
	if ( ! is_admin() && $query->is_main_query() && $query->is_tax( 'albums' ) ) {
		$query->set( 'post_status', [ 'publish', 'inherit' ] );
		$query->set( 'posts_per_page', 100 );
	}
	return $query;
} );Code language: PHP (php)

And then…

Now I have to do the hard part. Maybe there will be another post…

Categories
Sabbatical

Sabbatical Phase 2

As part of my work with Automattic, I’m granted a 3 month sabbatical after 5 years and I want to document some of the projects and things that I manage to do.

I haven’t gotten much done since the last post. Unfortunately my grandma passed away (aged 95 – she lived a great life and we’ll miss her) so there was a funeral to attend. Tami and I flew into Provo and picked up Jake, drove up to Idaho. The trip was fast and memorable — 32 of 35 grandchildren attended. Hopefully I’ll see many of them again 🤞.

Lindsay had a week of games (before the funeral) in Irvine. I spent 4 days driving back and forth for those. Other trips to Redlands and around SoCal have also occurred.

The house needed to be prepped for Christmas. I got all the decorations out of storage and setup the garlands, lights and trees.

Jake came home for Thanksgiving. We went surfing twice. Tami cooked a fantastic meal. We watched movies (Wicked, Moana 2, Rez Ball) and had an enjoyable time together.

I was asked to become a lay-leader in our local church organization and started assuming some of those duties.

Tech Fun

  • Updated MerPress plugin to Mermaid 11.4.1

Overall Stats

StatCount
Books read1 (no change)
Video games played3 (+1)
Surf trips3 (+2)
Movies (in theater)2 (+2)
Categories
Sabbatical

Sabbatical Phase 1

As part of my work with Automattic, I’m granted a 3 month sabbatical after 5 years and I started it recently. I want to document some of the projects and things that I manage to do.

It might make sense to report each week — meaning I report back each week on a specific cadence. Great for ensuring something gets posted, but I’m not sure I’ll have enough to report each week. (Progress on video games is not be terribly riveting/memorable).

The first “phase” cleared maintenance tasks around the house I’ve wanted to tackle but haven’t prioritized high enough.

  • Clean/organize garage
  • Clean/organize office
  • Flush water heater
  • Fix some yard things (lights, bushes, grass, etc.)

I finally completed building a LEGO Delorean set that I got for Christmas last year.

Lindsay organized a beach day/bonfire on Monday. I chaperoned by mainly staying out of the way and surfing — it did take a whole day though.

I started playing Rocksmith to improve my guitar skills. I haven’t been very regular with my practice so I’m ramping up slowly.

Our friend’s Father passed away and I’m covering their church class this week. This is a daily seminary/scripture study course for high school students.

Needs improvement

I haven’t gotten many books completed and I would like to make a habit of getting some regular study in.

Overall Stats

StatCount
Books read1
Video games played2
Surf trips1

Categories
Software Development

Running PHPUnit Tests with WordPress Plugins (easily)

If you are developing plugins for WordPress, one of the pain points I’ve struggled with is getting tests running. In the future, I need to document getting javascript tests running. Today, I’m going to outline the method for running phpunit tests. I will also discuss several problems I encountered along the way.

First: use @wordpress/env

This tool deserves a post all on it’s own but long story short, it allows a quick method to bootstrap a local development environment using docker. After installation it’s available as wp-env or npx wp-env (and I’ll be using the shorter version in this post).

The most basic “working” version of this is a .wp-env.json file in your plugin directory that looks like:

{
	"plugins": [ "." ]
}Code language: JSON / JSON with Comments (json)

Launch the environment with wp-env start. (WordPress is downloaded and stored locally. Next it creates docker-compose file mapping several containers: wordpress, cli, mysql, tests-wordpress, tests-cli, and tests-mysql.

Second: use wp-cli to create the scaffolded plugin-tests

> wp-env run tests-cli wp scaffold plugin-tests $(basename $(pwd))
Success: Created test files.
✔ Ran `wp scaffold plugin-tests sample-plugin` in 'tests-cli'. (in 24s 436ms)Code language: Shell Session (shell)

The above command creates a bunch of files that aren’t technically necessary and you can remove them (e.g. .circleci, bin/install-wp-tests.sh, .phpcs.xml.dist).

By passing the --ci parameter you can change the continuous integration provider. With wp-env you’ll need to add -- --ci=github.

Third: get it running

In theory you should be able to run wp-env run tests-cli phpunit, but that runs in the wrong directory. Solution: pass the current working directory.

> wp-env run tests-cli --env-cwd=wp-content/plugins/sample-plugin phpunit
ℹ Starting 'phpunit' on the tests-cli container.

Error: The PHPUnit Polyfills library is a requirement for running the WP test suite.
If you are trying to run plugin/theme integration tests, make sure the PHPUnit Polyfills library (https://github.com/Yoast/PHPUnit-Polyfills) is available and either load the autoload file of this library in your own test bootstrap before calling the WP Core test bootstrap file; or set the absolute path to the PHPUnit Polyfills library in a "WP_TESTS_PHPUNIT_POLYFILLS_PATH" constant to allow the WP Core bootstrap to load the Polyfills.

If you are trying to run the WP Core tests, make sure to set the "WP_RUN_CORE_TESTS" constant to 1 and run `composer update -W` before running the tests.
Once the dependencies are installed, you can run the tests using the Composer-installed version of PHPUnit or using a PHPUnit phar file, but the dependencies do need to be installed whichever way the tests are run.
✖ Command failed with exit code 1
Command failed with exit code 1Code language: Shell Session (shell)

Whoops, it looks like some libraries are missing. Let’s add them.

composer require --dev yoast/phpunit-polyfillsCode language: JavaScript (javascript)

That installs the library, but also gives us a version of phpunit we need to use to ensure they are loaded. With that slight change we see:

> wp-env run tests-cli --env-cwd=wp-content/plugins/sample-plugin vendor/bin/phpunit
ℹ Starting 'vendor/bin/phpunit' on the tests-cli container.

Installing...
Running as single site... To run multisite, use -c tests/phpunit/multisite.xml
[03-Oct-2024 18:17:14 UTC] PHP Warning:  Class "PHPUnit\Framework\Error\Deprecated" not found in /wordpress-phpunit/includes/phpunit6/compat.php on line 8
[03-Oct-2024 18:17:14 UTC] PHP Warning:  Class "PHPUnit\Framework\Error\Notice" not found in /wordpress-phpunit/includes/phpunit6/compat.php on line 9
[03-Oct-2024 18:17:14 UTC] PHP Warning:  Class "PHPUnit\Framework\Error\Warning" not found in /wordpress-phpunit/includes/phpunit6/compat.php on line 10
[03-Oct-2024 18:17:14 UTC] PHP Warning:  Class "PHPUnit\Framework\Warning" not found in /wordpress-phpunit/includes/phpunit6/compat.php on line 12
[03-Oct-2024 18:17:14 UTC] PHP Warning:  Class "PHPUnit\Framework\TestListener" not found in /wordpress-phpunit/includes/phpunit6/compat.php on line 15
Not running ajax tests. To execute these, use --group ajax.
Not running ms-files tests. To execute these, use --group ms-files.
Not running external-http tests. To execute these, use --group external-http.
PHPUnit 11.3.6 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.2.24
Configuration: /var/www/html/wp-content/plugins/sample-plugin/phpunit.xml.dist

No tests executed!
✔ Ran `vendor/bin/phpunit` in 'tests-cli'. (in 1s 34ms)Code language: Shell Session (shell)

Why weren’t any tests run?

Because WordPress doesn’t like the latest version of phpunit. Let’s change it with composer require -W --dev phpunit/phpunit ^9.6

> wp-env run tests-cli --env-cwd=wp-content/plugins/sample-plugin vendor/bin/phpunit
ℹ Starting 'vendor/bin/phpunit' on the tests-cli container.

Installing...
Running as single site... To run multisite, use -c tests/phpunit/multisite.xml
Not running ajax tests. To execute these, use --group ajax.
Not running ms-files tests. To execute these, use --group ms-files.
Not running external-http tests. To execute these, use --group external-http.
PHPUnit 9.6.21 by Sebastian Bergmann and contributors.

No tests executed!
✔ Ran `vendor/bin/phpunit` in 'tests-cli'. (in 1s 32ms)Code language: Shell Session (shell)

Still no tests… 🤔

Oh! the phpunit.xml.dist is excluding the only test. Remove that line:

--- phpunit.xml.dist	2024-10-03 11:28:06.212985211 -0700
+++ 2phpunit.xml.dist	2024-10-03 11:28:39.641072205 -0700
@@ -1,16 +1,15 @@
 <?xml version="1.0"?>
 <phpunit
 	bootstrap="tests/bootstrap.php"
 	backupGlobals="false"
 	colors="true"
 	convertErrorsToExceptions="true"
 	convertNoticesToExceptions="true"
 	convertWarningsToExceptions="true"
 	>
 	<testsuites>
 		<testsuite name="testing">
 			<directory prefix="test-" suffix=".php">./tests/</directory>
-			<exclude>./tests/test-sample.php</exclude>
 		</testsuite>
 	</testsuites>
 </phpunit>Code language: Diff (diff)

And success! 🎉

> wp-env run tests-cli --env-cwd=wp-content/plugins/sample-plugin vendor/bin/phpunit
ℹ Starting 'vendor/bin/phpunit' on the tests-cli container.

Installing...
Running as single site... To run multisite, use -c tests/phpunit/multisite.xml
Not running ajax tests. To execute these, use --group ajax.
Not running ms-files tests. To execute these, use --group ms-files.
Not running external-http tests. To execute these, use --group external-http.
PHPUnit 9.6.21 by Sebastian Bergmann and contributors.

Test 'SampleTest::test_sample' started
Test 'SampleTest::test_sample' ended


Time: 00:00.021, Memory: 38.50 MB

OK (1 test, 1 assertion)
✔ Ran `vendor/bin/phpunit` in 'tests-cli'. (in 0s 927ms)Code language: Shell Session (shell)

Other investigations/changes

  • phpunit 10 changes the way it finds tests. In the future, the file name and the class will need to agree (e.g. rename file to SampleTest.php and change phpunit.xml.dist to find files like *Test.php).
  • WordPress needs to update its support of phpunit too.
  • If you are using a javascript build process:
    • Add @wordpress/env as a development requirement.
    • Add a script to your package.json to run tests: "test": "wp-env run tests-cli --env-cwd=wp-content/plugins/sample-plugin vendor/bin/phpunit"
    • Now you can just run npm test!
  • Need more investigation about using wp-env in continuous integration environment.

Fin

As of October 3, 2024, these steps worked on getting an up-to-date sample plugin running phpunit tests using wp-env.

Categories
Uncategorized

California Math

Quick post to highlight this article that I found on Ycombinator:

https://www.noahpinion.blog/p/california-needs-real-math-education

I’ve helped my kids who are very smart try to understand math concepts that they don’t seem prepared for because the curriculum is not providing the foundations required for future success.

In response to lagging math scores, educators in California have been trying to water down math education — banning students from taking algebra in 8th grade, replacing advanced algebra classes with “data science” courses that don’t even teach the algebra required to understand basic statistics, and so on.