Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make unit tests pass on Windows #839

Merged
merged 12 commits into from
Jan 28, 2025
4 changes: 1 addition & 3 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ steps:
- github_commit_status:
context: Unit Tests

- label: Experiment Windows Unit Tests
# Reference: https://github.com/Automattic/studio/pull/76#issuecomment-2162689679
skip: Disabled till we solve Windows-specific test failures
- label: Windows Unit Tests
command: |
bash .buildkite/commands/install-node-dependencies.sh
echo '--- :npm: Run Unit Tests'
Expand Down
1 change: 1 addition & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module.exports = {
MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: 'main-window-preload-webpack-entry',
},
testRegex: '(/tests/.*|(\\.|/)(test|spec))\\.tsx?$',
testPathIgnorePatterns: [ '/node_modules/', 'tests/utils/' ],
moduleFileExtensions: [ 'ts', 'tsx', 'js', 'jsx', 'json', 'node' ],
globalSetup: '<rootDir>/jest-global-setup.ts',
setupFilesAfterEnv: [ '<rootDir>/jest-setup.ts' ],
Expand Down
2 changes: 1 addition & 1 deletion src/lib/import-export/import/validators/local-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class LocalValidator extends EventEmitter implements Validator {
plugins: [],
themes: [],
},
wpContentDirectory: 'app/public/wp-content',
wpContentDirectory: path.normalize( 'app/public/wp-content' ),
};
/* File rules:
* - Accept .zip
Expand Down
163 changes: 98 additions & 65 deletions src/lib/import-export/tests/export/exporters/default-exporter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import fsPromises from 'fs/promises';
import os from 'os';
import archiver from 'archiver';
import { format } from 'date-fns';
import { platformTestSuite } from 'src/tests/utils/platform-test-suite';
import { getWordPressVersionFromInstallation } from '../../../../../lib/wp-versions';
import { SiteServer } from '../../../../../site-server';
import { DefaultExporter } from '../../../export/exporters';
Expand Down Expand Up @@ -42,47 +43,72 @@ jest.mock( 'archiver', () => {
// Mock SiteServer
jest.mock( '../../../../../site-server' );

describe( 'DefaultExporter', () => {
const defaultTableNames = [
'wp_commentmeta',
'wp_comments',
'wp_links',
'wp_options',
'wp_postmeta',
'wp_posts',
'wp_term_relationships',
'wp_term_taxonomy',
'wp_termmeta',
'wp_terms',
];

platformTestSuite( 'DefaultExporter', ( { normalize } ) => {
let exporter: DefaultExporter;
let mockBackup: BackupContents;
let mockOptions: ExportOptions;
let mockArchiver: jest.Mocked< PartialArchiver >;
let mockWriteStream: { on: jest.Mock; path: string };

const mockFiles = [
{ path: '/path/to/site/wp-content/uploads', name: 'file1.jpg', isFile: () => true },
{ path: '/path/to/site', name: 'wp-config.php', isFile: () => true },
{ path: '/path/to/site/wp-content/plugins/plugin1', name: 'plugin1.php', isFile: () => true },
{ path: '/path/to/site/wp-content/themes/theme1', name: 'index.php', isFile: () => true },
{ path: '/path/to/site/wp-includes/index.php', name: 'index.php', isFile: () => true },
{ path: '/path/to/site', name: 'wp-load.php', isFile: () => true },
];

const defaultTableNames = [
'wp_commentmeta',
'wp_comments',
'wp_links',
'wp_options',
'wp_postmeta',
'wp_posts',
'wp_term_relationships',
'wp_term_taxonomy',
'wp_termmeta',
'wp_terms',
];

( fsPromises.readdir as jest.Mock ).mockResolvedValue( mockFiles );
( getWordPressVersionFromInstallation as jest.Mock ).mockResolvedValue( '6.6.1' );

beforeEach( () => {
const mockFiles = [
{
path: normalize( '/path/to/site/wp-content/uploads' ),
name: 'file1.jpg',
isFile: () => true,
},
{
path: normalize( '/path/to/site' ),
name: 'wp-config.php',
isFile: () => true,
},
{
path: normalize( '/path/to/site/wp-content/plugins/plugin1' ),
name: 'plugin1.php',
isFile: () => true,
},
{
path: normalize( '/path/to/site/wp-content/themes/theme1' ),
name: 'index.php',
isFile: () => true,
},
{
path: normalize( '/path/to/site/wp-includes/index.php' ),
name: 'index.php',
isFile: () => true,
},
{
path: normalize( '/path/to/site' ),
name: 'wp-load.php',
isFile: () => true,
},
];

( fsPromises.readdir as jest.Mock ).mockResolvedValue( mockFiles );

mockBackup = {
backupFile: '/path/to/backup.tar.gz',
wpConfigFile: '/path/to/wp-config.php',
sqlFiles: [ '/tmp/studio_export_123/file.sql' ],
backupFile: normalize( '/path/to/backup.tar.gz' ),
wpConfigFile: normalize( '/path/to/wp-config.php' ),
sqlFiles: [ normalize( '/tmp/studio_export_123/file.sql' ) ],
wpContent: {
uploads: [ '/path/to/wp-content/uploads/file1.jpg' ],
plugins: [ '/path/to/wp-content/plugins/plugin1' ],
themes: [ '/path/to/wp-content/themes/theme1' ],
uploads: [ normalize( '/path/to/wp-content/uploads/file1.jpg' ) ],
plugins: [ normalize( '/path/to/wp-content/plugins/plugin1' ) ],
themes: [ normalize( '/path/to/wp-content/themes/theme1' ) ],
},
};

Expand All @@ -91,10 +117,10 @@ describe( 'DefaultExporter', () => {
running: false,
id: '123',
name: '123',
path: '/path/to/site',
path: normalize( '/path/to/site' ),
phpVersion: '7.4',
},
backupFile: '/path/to/backup.tar.gz',
backupFile: normalize( '/path/to/backup.tar.gz' ),
includes: {
uploads: true,
plugins: true,
Expand All @@ -108,7 +134,7 @@ describe( 'DefaultExporter', () => {
jest.clearAllMocks();

( SiteServer.get as jest.Mock ).mockReturnValue( {
details: { path: '/path/to/site' },
details: { path: normalize( '/path/to/site' ) },
executeWpCliCommand: jest.fn( function ( command: string ) {
switch ( true ) {
case /plugin list/.test( command ):
Expand All @@ -129,7 +155,7 @@ describe( 'DefaultExporter', () => {
);
mockWriteStream = {
on: jest.fn(),
path: '/path/to/backup.tar.gz',
path: normalize( '/path/to/backup.tar.gz' ),
};
( fs.createWriteStream as jest.Mock ).mockReturnValue( mockWriteStream );
( fsPromises.unlink as jest.Mock ).mockResolvedValue( undefined );
Expand Down Expand Up @@ -177,7 +203,7 @@ describe( 'DefaultExporter', () => {
const exporter = new DefaultExporter( options );
await exporter.export();

expect( mockArchiver.file ).toHaveBeenCalledWith( '/path/to/site/wp-config.php', {
expect( mockArchiver.file ).toHaveBeenCalledWith( normalize( '/path/to/site/wp-config.php' ), {
name: 'wp-config.php',
} );
} );
Expand All @@ -187,10 +213,13 @@ describe( 'DefaultExporter', () => {
await exporter.export();

expect( getWordPressVersionFromInstallation ).toHaveBeenCalledTimes( 1 );
expect( getWordPressVersionFromInstallation ).toHaveBeenCalledWith( '/path/to/site' );
expect( mockArchiver.file ).toHaveBeenCalledWith( '/tmp/studio_export_123/meta.json', {
name: 'meta.json',
} );
expect( getWordPressVersionFromInstallation ).toHaveBeenCalledWith(
normalize( '/path/to/site' )
);
expect( mockArchiver.file ).toHaveBeenCalledWith(
normalize( '/tmp/studio_export_123/meta.json' ),
{ name: 'meta.json' }
);
} );

it( 'should add wp-content files to the archive', async () => {
Expand All @@ -207,23 +236,25 @@ describe( 'DefaultExporter', () => {
const exporter = new DefaultExporter( options );
await exporter.export();
// Check each expected call individually
expect( mockArchiver.file ).toHaveBeenNthCalledWith( 1, '/path/to/site/wp-config.php', {
name: 'wp-config.php',
} );
expect( mockArchiver.file ).toHaveBeenNthCalledWith(
1,
normalize( '/path/to/site/wp-config.php' ),
{ name: 'wp-config.php' }
);
expect( mockArchiver.file ).toHaveBeenNthCalledWith(
2,
'/path/to/site/wp-content/uploads/file1.jpg',
{ name: 'wp-content/uploads/file1.jpg' }
normalize( '/path/to/site/wp-content/uploads/file1.jpg' ),
{ name: normalize( 'wp-content/uploads/file1.jpg' ) }
);
expect( mockArchiver.file ).toHaveBeenNthCalledWith(
3,
'/path/to/site/wp-content/plugins/plugin1/plugin1.php',
{ name: 'wp-content/plugins/plugin1/plugin1.php' }
normalize( '/path/to/site/wp-content/plugins/plugin1/plugin1.php' ),
{ name: normalize( 'wp-content/plugins/plugin1/plugin1.php' ) }
);
expect( mockArchiver.file ).toHaveBeenNthCalledWith(
4,
'/path/to/site/wp-content/themes/theme1/index.php',
{ name: 'wp-content/themes/theme1/index.php' }
normalize( '/path/to/site/wp-content/themes/theme1/index.php' ),
{ name: normalize( 'wp-content/themes/theme1/index.php' ) }
);
} );

Expand All @@ -237,20 +268,20 @@ describe( 'DefaultExporter', () => {
database: true,
},
};
( fsPromises.mkdtemp as jest.Mock ).mockResolvedValue( '/tmp/studio_export_123' );
( fsPromises.mkdtemp as jest.Mock ).mockResolvedValue( normalize( '/tmp/studio_export_123' ) );

const exporter = new DefaultExporter( options );
await exporter.export();

expect( mockArchiver.file ).toHaveBeenNthCalledWith( 1, '/path/to/site/wp-config.php', {
name: 'wp-config.php',
} );
expect( mockArchiver.file ).toHaveBeenNthCalledWith(
1,
normalize( '/path/to/site/wp-config.php' ),
{ name: 'wp-config.php' }
);
expect( mockArchiver.file ).toHaveBeenNthCalledWith(
2,
'/tmp/studio_export_123/studio-backup-db-export-2023-07-31-12-00-00.sql',
{
name: 'sql/studio-backup-db-export-2023-07-31-12-00-00.sql',
}
normalize( '/tmp/studio_export_123/studio-backup-db-export-2023-07-31-12-00-00.sql' ),
{ name: 'sql/studio-backup-db-export-2023-07-31-12-00-00.sql' }
);
} );

Expand All @@ -265,18 +296,20 @@ describe( 'DefaultExporter', () => {
},
splitDatabaseDumpByTable: true,
};
( fsPromises.mkdtemp as jest.Mock ).mockResolvedValue( '/tmp/studio_export_123' );
( fsPromises.mkdtemp as jest.Mock ).mockResolvedValue( normalize( '/tmp/studio_export_123' ) );

const exporter = new DefaultExporter( options );
await exporter.export();

expect( mockArchiver.file ).toHaveBeenNthCalledWith( 1, '/path/to/site/wp-config.php', {
name: 'wp-config.php',
} );
expect( mockArchiver.file ).toHaveBeenNthCalledWith(
1,
normalize( '/path/to/site/wp-config.php' ),
{ name: 'wp-config.php' }
);

for ( const tableName of defaultTableNames ) {
expect( mockArchiver.file ).toHaveBeenCalledWith(
`/tmp/studio_export_123/${ tableName }.sql`,
normalize( `/tmp/studio_export_123/${ tableName }.sql` ),
{ name: `sql/${ tableName }.sql` }
);
}
Expand All @@ -289,12 +322,12 @@ describe( 'DefaultExporter', () => {
} );

it( 'should cleanup temporary files when database is included', async () => {
mockBackup.sqlFiles = [ '/tmp/studio_export_123/file.sql' ];
mockBackup.sqlFiles = [ normalize( '/tmp/studio_export_123/file.sql' ) ];

await exporter.export();

expect( fsPromises.unlink ).toHaveBeenCalledWith(
'/tmp/studio_export_123/studio-backup-db-export-2023-07-31-12-00-00.sql'
normalize( '/tmp/studio_export_123/studio-backup-db-export-2023-07-31-12-00-00.sql' )
);
} );

Expand All @@ -316,7 +349,7 @@ describe( 'DefaultExporter', () => {
it( 'should return false when canHandle is called with invalid options', async () => {
const exporter = new DefaultExporter( {
...mockOptions,
backupFile: '/path/to/backup.sql',
backupFile: normalize( '/path/to/backup.sql' ),
} );

const canHandle = await exporter.canHandle();
Expand All @@ -325,7 +358,7 @@ describe( 'DefaultExporter', () => {

it( 'should fail when can not get plugin or theme details', async () => {
( SiteServer.get as jest.Mock ).mockReturnValue( {
details: { path: '/path/to/site' },
details: { path: normalize( '/path/to/site' ) },
executeWpCliCommand: jest.fn( function ( command: string ) {
switch ( true ) {
case /plugin list/.test( command ):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { move } from 'fs-extra';
import { platformTestSuite } from 'src/tests/utils/platform-test-suite';
import { SiteServer } from '../../../../../site-server';
import { SqlExporter } from '../../../export/exporters';
import { ExportOptions } from '../../../export/types';
Expand All @@ -11,7 +12,7 @@ jest.mock( 'fs-extra' );
// Mock SiteServer
jest.mock( '../../../../../site-server' );

describe( 'SqlExporter', () => {
platformTestSuite( 'SqlExporter', ( { normalize } ) => {
let exporter: SqlExporter;
let mockOptions: ExportOptions;

Expand All @@ -24,7 +25,7 @@ describe( 'SqlExporter', () => {
path: '/path/to/site',
phpVersion: '7.4',
},
backupFile: '/path/to/backup.sql',
backupFile: normalize( '/path/to/backup.sql' ),
includes: {
uploads: false,
plugins: false,
Expand All @@ -38,7 +39,7 @@ describe( 'SqlExporter', () => {
jest.clearAllMocks();

( SiteServer.get as jest.Mock ).mockReturnValue( {
details: { path: '/path/to/site' },
details: { path: normalize( '/path/to/site' ) },
executeWpCliCommand: jest.fn().mockResolvedValue( { stderr: null } ),
} );
( move as jest.Mock ).mockResolvedValue( null );
Expand All @@ -59,16 +60,14 @@ describe( 'SqlExporter', () => {
const siteServer = SiteServer.get( '123' );
expect( siteServer?.executeWpCliCommand ).toHaveBeenCalledWith(
'sqlite export studio-backup-db-export-2024-08-01-12-00-00.sql --require=/tmp/sqlite-command/command.php',
{
skipPluginsAndThemes: true,
}
{ skipPluginsAndThemes: true }
);
} );

it( 'should call move on the temporary file', async () => {
await exporter.export();
expect( move ).toHaveBeenCalledWith(
'/path/to/site/studio-backup-db-export-2024-08-01-12-00-00.sql',
normalize( '/path/to/site/studio-backup-db-export-2024-08-01-12-00-00.sql' ),
mockOptions.backupFile
);
} );
Expand All @@ -81,7 +80,7 @@ describe( 'SqlExporter', () => {
it( 'should return false when canHandle is called with invalid options', async () => {
const exporter = new SqlExporter( {
...mockOptions,
backupFile: '/path/to/backup.zip',
backupFile: normalize( '/path/to/backup.zip' ),
} );

const canHandle = await exporter.canHandle();
Expand Down
Loading
Loading