diff --git a/packages/wp-now/src/tests/download-with-timer.ts b/packages/wp-now/src/tests/download-with-timer.ts new file mode 100644 index 00000000..3e287d89 --- /dev/null +++ b/packages/wp-now/src/tests/download-with-timer.ts @@ -0,0 +1,7 @@ +export async function downloadWithTimer(name, fn) { + console.log(`Downloading ${name}...`); + console.time(name); + await fn(); + console.log(`${name} downloaded.`); + console.timeEnd(name); +} diff --git a/packages/wp-now/src/tests/update-plugin-updates.spec.ts b/packages/wp-now/src/tests/update-plugin-updates.spec.ts new file mode 100644 index 00000000..736c511e --- /dev/null +++ b/packages/wp-now/src/tests/update-plugin-updates.spec.ts @@ -0,0 +1,215 @@ +import startWPNow from '../wp-now'; +import { getWpNowPath } from '../'; +import getWpNowConfig from '../config'; +import fs from 'fs-extra'; +import path from 'path'; +import { + downloadSqliteIntegrationPlugin, + downloadWordPress, +} from '../download'; +import os from 'os'; +import crypto from 'crypto'; +import { downloadWithTimer } from './download-with-timer'; +import getWpNowTmpPath from '../get-wp-now-tmp-path'; + +const exampleDir = __dirname + '/mode-examples'; + +async function copyWordPressAndStart(tmpDirectory: string, folderName: string) { + // Copy whole WordPress directory to a temporary directory + const wordPressDir = path.join( + getWpNowPath(), + 'wordpress-versions', + 'latest' + ); + const projectPath = path.join(tmpDirectory, folderName); + fs.copySync(wordPressDir, projectPath); + + const options = await getWpNowConfig({ path: projectPath }); + return await startWPNow(options); +} + +describe('Test WordPress plugin updates', () => { + let tmpExampleDirectory; + + /** + * Download an initial copy of WordPress + */ + beforeAll(async () => { + fs.rmSync(getWpNowTmpPath(), { recursive: true, force: true }); + await Promise.all([ + downloadWithTimer('wordpress', downloadWordPress), + downloadWithTimer('sqlite', downloadSqliteIntegrationPlugin), + ]); + }); + + /** + * Copy example directory to a temporary directory + */ + beforeEach(() => { + const tmpDirectory = os.tmpdir(); + const directoryHash = crypto.randomBytes(20).toString('hex'); + + tmpExampleDirectory = path.join( + tmpDirectory, + `wp-now-tests-examples-${directoryHash}` + ); + fs.ensureDirSync(tmpExampleDirectory); + fs.copySync(exampleDir, tmpExampleDirectory); + }); + + /** + * Remove temporary directory + */ + afterEach(() => { + fs.rmSync(tmpExampleDirectory, { recursive: true, force: true }); + }); + + /** + * Remove wp-now hidden directory from temporary directory. + */ + afterAll(() => { + fs.rmSync(getWpNowTmpPath(), { recursive: true, force: true }); + }); + + test('move a directory', async () => { + const { php } = await copyWordPressAndStart( + tmpExampleDirectory, + 'wordpress-move-directory' + ); + const code = `exists($path); + echo "exists before " . ($result ? 'true' : 'false') . "\n"; + + $result = move_dir($path, "./wp-content/other-path", true); + echo "moved " . ($result ? 'true' : 'false') . "\n"; + + $result = $wp_filesystem->exists($path); + echo "exists after " . ($result ? 'true' : 'false') . "\n"; + `; + const result = await php.run({ + code, + }); + + expect(result.text).toMatch('Path: wp-content/plugins/akismet'); + expect(result.text).toMatch('exists before true'); + expect(result.text).toMatch('exists after false'); + }); + + test('delete a directory', async () => { + const { php } = await copyWordPressAndStart( + tmpExampleDirectory, + 'wordpress-move-directory' + ); + const code = `exists($path); + echo "exists before " . ($result ? 'true' : 'false') . "\n"; + + $deleted = $wp_filesystem->delete( $path, true ); + echo "deleted " . ($deleted ? 'true' : 'false') . "\n"; + + $result = $wp_filesystem->exists($path); + echo "exists after " . ($result ? 'true' : 'false') . "\n"; + `; + const result = await php.run({ + code, + }); + + expect(result.text).toMatch('Path: wp-content/plugins/akismet'); + expect(result.text).toMatch('exists before true'); + expect(result.text).toMatch('deleted true'); + expect(result.text).toMatch('exists after false'); + }); + + test('update plugin', async () => { + const { + php, + options: { projectPath }, + } = await copyWordPressAndStart( + tmpExampleDirectory, + 'wordpress-plugin-update' + ); + + const akismetExistsBeforeUpdate = fs.existsSync( + path.join(projectPath, 'wp-content/plugins/akismet/akismet.php') + ); + expect(akismetExistsBeforeUpdate).toBe(true); + + const code = `exists($plugin_file_path); + echo "exists before update " . ($result ? 'true' : 'false') . "\n"; + + // Replace the akismet/akismet.php to a lower version to force an update + $plugin_file_content = $wp_filesystem->get_contents($plugin_file_path); + $plugin_file_content = preg_replace('/Version: (d|.)+/', 'Version: 5.3.1', $plugin_file_content); + $wp_filesystem->put_contents($plugin_file_path, $plugin_file_content); + + wp_update_plugins(); + + $upgrader = new Plugin_Upgrader(); + $upgrader->upgrade( $plugin ); + + echo "\n"; + $result = $wp_filesystem->exists($plugin_file_path); + echo "exists after update " . ($result ? 'true' : 'false') . "\n"; + `; + + const result = await php.run({ + code, + }); + + /* Example response: + exists before update true +

Downloading update from https://downloads.wordpress.org/plugin/akismet.5.3.2.zip

+

Unpacking the update…

+

Installing the latest version…

+

Removing the old version of the plugin…

+

Could not remove the old plugin.

+

Plugin update failed.

+
+ exists after update true + */ + + const akismetExistsAfterUpdate = fs.existsSync( + path.join(projectPath, 'wp-content/plugins/akismet/akismet.php') + ); + expect(akismetExistsAfterUpdate).toBe(true); + + expect(result.text).toMatch('exists before update true'); + expect(result.text).toMatch('Removing the old version of the plugin'); + expect(result.text).not.toMatch('Could not remove the old plugin'); + expect(result.text).not.toMatch('Plugin update failed'); + expect(result.text).toMatch('exists after update true'); + }); +}); diff --git a/packages/wp-now/src/tests/wp-now.spec.ts b/packages/wp-now/src/tests/wp-now.spec.ts index c29d9d79..cae420c6 100644 --- a/packages/wp-now/src/tests/wp-now.spec.ts +++ b/packages/wp-now/src/tests/wp-now.spec.ts @@ -21,17 +21,10 @@ import getWpNowTmpPath from '../get-wp-now-tmp-path'; import getWpCliTmpPath from '../get-wp-cli-tmp-path'; import { executeWPCli } from '../execute-wp-cli'; import { runCli } from '../run-cli'; +import { downloadWithTimer } from './download-with-timer'; const exampleDir = __dirname + '/mode-examples'; -async function downloadWithTimer(name, fn) { - console.log(`Downloading ${name}...`); - console.time(name); - await fn(); - console.log(`${name} downloaded.`); - console.timeEnd(name); -} - // Options test('getWpNowConfig with default options', async () => { const projectDirectory = exampleDir + '/index'; diff --git a/packages/wp-now/vite.config.ts b/packages/wp-now/vite.config.ts index 2f5d1af5..29076df9 100644 --- a/packages/wp-now/vite.config.ts +++ b/packages/wp-now/vite.config.ts @@ -25,6 +25,7 @@ export default defineConfig(() => { }, environment: 'jsdom', include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + threads: false, }, }; });