diff --git a/packages/lib/fsDriver.test.ts b/packages/lib/fsDriver.test.ts index 5984679ed0..6975e64eca 100644 --- a/packages/lib/fsDriver.test.ts +++ b/packages/lib/fsDriver.test.ts @@ -9,7 +9,7 @@ const windowsPartitionLetter = __filename[0]; // /tmp/file.txt to {partition}:\tmp\file.txt function platformPath(path: string) { if (shim.isWindows()) { - return `${windowsPartitionLetter}:${path.replace(/\//g, '\\')}`; + return `${windowsPartitionLetter.toLowerCase()}:${path.replace(/\//g, '\\')}`; } else { return path; } diff --git a/packages/lib/services/rest/Api.test.ts b/packages/lib/services/rest/Api.test.ts index 8fc6386f26..a1aca31466 100644 --- a/packages/lib/services/rest/Api.test.ts +++ b/packages/lib/services/rest/Api.test.ts @@ -12,6 +12,9 @@ import ResourceService from '../../services/ResourceService'; import SearchEngine from '../search/SearchEngine'; const { MarkupToHtml } = require('@joplin/renderer'); import { NoteEntity, ResourceEntity } from '../database/types'; +import { toFileProtocolPath } from '@joplin/utils/path'; +import { join } from 'path'; +import { htmlentities } from '@joplin/utils/html'; const createFolderForPagination = async (num: number, time: number) => { await Folder.save({ @@ -512,10 +515,11 @@ describe('services/rest/Api', () => { let response = null; const f = await Folder.save({ title: 'pdf test1' }); + const url = toFileProtocolPath(join(supportDir, 'welcome.pdf')); response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({ title: 'testing PDF embeds', parent_id: f.id, - body_html: `
`, + body_html: `
`, })); const resources = await Resource.all(); diff --git a/packages/lib/urlUtils.test.js b/packages/lib/urlUtils.test.js index 8d53620088..2b6c349401 100644 --- a/packages/lib/urlUtils.test.js +++ b/packages/lib/urlUtils.test.js @@ -57,20 +57,33 @@ describe('urlUtils', () => { [ 'file:///home/builder/.config/joplindev-desktop/profile-owmhbsat/resources/4a12670298dd46abbb140ffc8a10b583.md', '/home/builder/.config/joplindev-desktop/profile-owmhbsat/resources', + 'posix', { itemId: '4a12670298dd46abbb140ffc8a10b583', hash: '' }, ], [ 'file:///home/builder/.config/joplindev-desktop/profile-owmhbsat/resources/4a12670298dd46abbb140ffc8a10b583.md5#foo', '/home/builder/.config/joplindev-desktop/profile-owmhbsat/resources', + 'posix', { itemId: '4a12670298dd46abbb140ffc8a10b583', hash: 'foo' }, ], [ 'file:///home/builder/.config/joplindev-desktop/profile-owmhbsat/resources/4a12670298dd46abbb140ffc8a10b583.png?t=12345', '/home/builder/.config/joplindev-desktop/profile-owmhbsat/resources', + 'posix', { itemId: '4a12670298dd46abbb140ffc8a10b583', hash: '' }, ], - ])('should detect resource file URLs', (url, resourceDir, expected) => { - expect(urlUtils.parseResourceUrl(urlUtils.fileUrlToResourceUrl(url, resourceDir))).toMatchObject(expected); + [ + 'file:///C:/Users/Test/.config/joplin-desktop/profile-owmhbsat/resources/a1234567890123456789012345678901.png?t=12345', + 'C:\\Users\\Test\\.config\\joplin-desktop\\profile-owmhbsat\\resources', + 'win32', + { itemId: 'a1234567890123456789012345678901', hash: '' }, + ], + ])('should detect resource file URLs (case %#)', (url, resourceDir, os, expected) => { + expect( + urlUtils.parseResourceUrl( + urlUtils.fileUrlToResourceUrl(url, resourceDir, os), + ), + ).toMatchObject(expected); }); it('should extract resource URLs', (async () => { diff --git a/packages/lib/urlUtils.ts b/packages/lib/urlUtils.ts index 6125bde49a..4c56cc224a 100644 --- a/packages/lib/urlUtils.ts +++ b/packages/lib/urlUtils.ts @@ -72,8 +72,8 @@ export const parseResourceUrl = (url: string) => { }; }; -export const fileUrlToResourceUrl = (fileUrl: string, resourceDir: string) => { - let resourceDirUrl = toFileProtocolPath(resourceDir); +export const fileUrlToResourceUrl = (fileUrl: string, resourceDir: string, os: string|null = null) => { + let resourceDirUrl = toFileProtocolPath(resourceDir, os); if (!resourceDirUrl.endsWith('/')) { resourceDirUrl += '/'; } diff --git a/packages/lib/utils/resolvePathWithinDir.test.ts b/packages/lib/utils/resolvePathWithinDir.test.ts index c6a08953ac..8d60519f1a 100644 --- a/packages/lib/utils/resolvePathWithinDir.test.ts +++ b/packages/lib/utils/resolvePathWithinDir.test.ts @@ -1,3 +1,4 @@ +import { posix, win32 } from 'node:path'; import resolvePathWithinDir from './resolvePathWithinDir'; describe('resolvePathWithinDir', () => { @@ -23,16 +24,16 @@ describe('resolvePathWithinDir', () => { ]; for (const testCase of testCases) { - expect(resolvePathWithinDir(testCase.baseDir, testCase.path, false)).toBe(testCase.expected); + expect(resolvePathWithinDir(testCase.baseDir, testCase.path, posix)).toBe(testCase.expected); } }); test('should return correct values for Windows-style paths', () => { - expect(resolvePathWithinDir('C:\\a\\test\\path', 'C:\\a\\test\\path\\2', true)).toBe('C:\\a\\test\\path\\2'); - expect(resolvePathWithinDir('C:\\\\a\\test\\path', '.\\path\\2', true)).toBe('C:\\a\\test\\path\\path\\2'); - expect(resolvePathWithinDir('C:\\a\\test\\path', '..\\path\\2', true)).toBe('C:\\a\\test\\path\\2'); - expect(resolvePathWithinDir('C:\\a\\test\\path', '..\\2', true)).toBe(null); - expect(resolvePathWithinDir('D:\\a\\test\\path', 'C:\\a\\test\\path\\2', true)).toBe(null); - expect(resolvePathWithinDir('\\a\\test\\path', 'D:\\a\\test\\path\\2', true)).toBe(null); + expect(resolvePathWithinDir('C:\\a\\test\\path', 'C:\\a\\test\\path\\2', win32)).toBe('C:\\a\\test\\path\\2'); + expect(resolvePathWithinDir('C:\\\\a\\test\\path', '.\\path\\2', win32)).toBe('C:\\a\\test\\path\\path\\2'); + expect(resolvePathWithinDir('C:\\a\\test\\path', '..\\path\\2', win32)).toBe('C:\\a\\test\\path\\2'); + expect(resolvePathWithinDir('C:\\a\\test\\path', '..\\2', win32)).toBe(null); + expect(resolvePathWithinDir('D:\\a\\test\\path', 'C:\\a\\test\\path\\2', win32)).toBe(null); + expect(resolvePathWithinDir('\\a\\test\\path', 'D:\\a\\test\\path\\2', win32)).toBe(null); }); }); diff --git a/packages/lib/utils/resolvePathWithinDir.ts b/packages/lib/utils/resolvePathWithinDir.ts index be32c47f11..7e2cc45df0 100644 --- a/packages/lib/utils/resolvePathWithinDir.ts +++ b/packages/lib/utils/resolvePathWithinDir.ts @@ -12,13 +12,8 @@ const resolvePathWithinDir = ( baseDir: string, relativePath: string, // For testing - forceWin32Paths?: boolean, + pathModule = path, ) => { - let pathModule = path; - if (forceWin32Paths === true) { - pathModule = path.win32; - } - let resolvedBaseDir = pathModule.resolve(baseDir); const resolvedPath = pathModule.resolve(baseDir, relativePath); diff --git a/packages/server/src/db.migrations.test.ts b/packages/server/src/db.migrations.test.ts index a5e2d09cdd..bf22dc91e1 100644 --- a/packages/server/src/db.migrations.test.ts +++ b/packages/server/src/db.migrations.test.ts @@ -10,8 +10,12 @@ async function dbSchemaSnapshot(db: DbConnection): Promise { describe('db.migrations', () => { + let testIndex = 0; beforeEach(async () => { - await beforeAllDb('db.migrations', { autoMigrate: false }); + // Use `beforeAllDb` in `beforeEach` to ensure each test has its own database. + // To work around file locking issues on Windows, each test needs its own database instance: + const databaseKey = `db.migrations.${testIndex ++}`; + await beforeAllDb(databaseKey, { autoMigrate: false }); await beforeEachDb(); }); diff --git a/packages/server/src/db.replication.test.ts b/packages/server/src/db.replication.test.ts index 60ae7111b7..270fe8953d 100644 --- a/packages/server/src/db.replication.test.ts +++ b/packages/server/src/db.replication.test.ts @@ -21,8 +21,12 @@ const event2 = { id: eventId2, }; +let testIndex = 0; const beforeTest = async (envValues: Record = null) => { - await beforeAllDb('db.replication', envValues ? { envValues } : null); + // Use `beforeAllDb` in `beforeEach` to ensure each test has its own database. + // To work around file locking issues on Windows, each test needs its own database instance: + const databaseKey = `db.replication.${testIndex ++}`; + await beforeAllDb(databaseKey, envValues ? { envValues } : null); await beforeEachDb(); }; @@ -32,6 +36,10 @@ const afterTest = async () => { describe('db.replication', () => { + afterEach(async () => { + await afterTest(); + }); + it('should reconnect a database', async () => { if (getDatabaseClientType() === DatabaseConfigClient.PostgreSQL) return; @@ -57,13 +65,13 @@ describe('db.replication', () => { expect(results.length).toBe(2); expect([results[0].id, results[1].id].sort()).toEqual([eventId1, eventId2]); } - - await afterTest(); }); it('should manually sync an SQLite slave instance', async () => { if (getDatabaseClientType() === DatabaseConfigClient.PostgreSQL) return; + await beforeTest(); + const masterConfig: DatabaseConfig = { client: DatabaseConfigClient.SQLite, name: `${packageRootDir}/db-master-test.sqlite`, @@ -139,8 +147,6 @@ describe('db.replication', () => { expect(result.items.length).toBe(1); expect(result.items[0].type).toBe(ChangeType.Update); - - await afterTest(); }); }); diff --git a/packages/tools/checkLibPaths.test.ts b/packages/tools/checkLibPaths.test.ts index 6d5e5ce500..7c56e59aac 100644 --- a/packages/tools/checkLibPaths.test.ts +++ b/packages/tools/checkLibPaths.test.ts @@ -29,7 +29,7 @@ describe('checkLibPaths', () => { for (const testCase of testCases) { const [expected, input] = testCase; const actual = findInvalidImportPaths(__dirname, input.split('\n').map(l => l.trim()).join('\n')); - expect(actual.length).toBe(expected); + expect(actual).toHaveLength(expected); } }); diff --git a/packages/tools/checkLibPaths.ts b/packages/tools/checkLibPaths.ts index d811ba2af0..1a9ee8467a 100644 --- a/packages/tools/checkLibPaths.ts +++ b/packages/tools/checkLibPaths.ts @@ -4,6 +4,7 @@ import { readFile } from 'fs-extra'; import { normalize } from 'path'; import yargs = require('yargs'); import { dirname } from './tool-utils'; +import { toForwardSlashes } from '@joplin/utils/path'; export const findInvalidImportPaths = (baseDir: string, fileContent: string): string[] => { const output: string[] = []; @@ -26,7 +27,7 @@ export const findInvalidImportPaths = (baseDir: string, fileContent: string): st const matches = regex.exec(fileContent); if (!matches) break; const [line, packagePath] = matches; - const fullPath = normalize(`${baseDir}/${packagePath}`); + const fullPath = toForwardSlashes(normalize(`${baseDir}/${packagePath}`)); if (fullPath.includes('packages/lib/') || fullPath.includes('packages/renderer/')) output.push(line); } diff --git a/packages/transcribe/snapshot-resolver.js b/packages/transcribe/snapshot-resolver.js index 187e559e99..470deafd37 100644 --- a/packages/transcribe/snapshot-resolver.js +++ b/packages/transcribe/snapshot-resolver.js @@ -4,7 +4,7 @@ const path = require('path'); module.exports = { resolveSnapshotPath: (testPath, snapshotExtension) => { const srcPath = testPath - .replace(/dist\/src\//, 'src/') + .replace(/dist[/\\]src[/\\]/, 'src/') .replace(/\.js$/, ''); const snapshotPath = path.join( @@ -28,5 +28,6 @@ module.exports = { return testPath; }, - testPathForConsistencyCheck: '/dist/src/example.test.js', + // Normalize to prevent assertion failures on Windows: + testPathForConsistencyCheck: path.normalize('/dist/src/example.test.js'), };