TidGi-Desktop/features/supports/paths.ts
linonetwo a0e7a6ec57 Add mobile HTTP git sync tests & merge utilities
Introduce end-to-end tests and server-side support for mobile-style Smart HTTP git sync. Adds a new mobileSyncConflict.feature and extended sync.step definitions to simulate HTTP clone/sync cycles, pushes, and assertions (including file content checks and HTTP readiness/backoff). Introduces src/services/gitServer/mergeUtilities.ts to resolve .tid conflicts (mobile metadata wins, body merged) and common git helpers, and wires those into the git server (use runGitCollectStdout, DESKTOP_GIT_IDENTITY, and mergeAfterPush endpoint). Misc: update workspace restart flow and settings handling, tweak test helpers (skip screenshot capture for file steps), adjust slugify rules, minor UI/formatting change, and bump git-sync-js dependency (with lockfile update).
2026-02-24 16:50:08 +08:00

117 lines
4.1 KiB
TypeScript

import fs from 'fs';
import path from 'path';
import type { ApplicationWorld } from '../stepDefinitions/application';
export function getPackedAppPath(): string {
const platform = process.platform;
const outputDirectory = path.join(process.cwd(), 'out');
// Define possible app paths based on platform
const possiblePaths: string[] = [];
switch (platform) {
case 'win32':
possiblePaths.push(
path.join(outputDirectory, 'TidGi-win32-x64', 'tidgi.exe'),
path.join(outputDirectory, 'TidGi-win32-arm64', 'tidgi.exe'),
path.join(outputDirectory, 'TidGi-win32-ia32', 'tidgi.exe'),
);
break;
case 'darwin':
possiblePaths.push(
path.join(outputDirectory, 'TidGi-darwin-x64', 'TidGi.app', 'Contents', 'MacOS', 'TidGi'),
path.join(outputDirectory, 'TidGi-darwin-arm64', 'TidGi.app', 'Contents', 'MacOS', 'TidGi'),
);
break;
case 'linux':
possiblePaths.push(
path.join(outputDirectory, 'TidGi-linux-x64', 'tidgi'),
path.join(outputDirectory, 'TidGi-linux-arm64', 'tidgi'),
);
break;
default:
throw new Error(`Unsupported platform: ${platform}`);
}
// Find the first existing executable
for (const appPath of possiblePaths) {
if (fs.existsSync(appPath)) {
return appPath;
}
}
throw new Error(
`TidGi executable not found. Checked paths:\n${possiblePaths.join('\n')}\n\nYou should run \`pnpm run test:prepare-e2e\` before running the tests to ensure the app is built.`,
);
}
// Repo root used for packaging-relative resolution in tests.
export const repoRoot = path.resolve(process.cwd());
/**
* Archive-safe sanitization: generate a slug that is safe for zipping/unzipping across platforms.
* This is a re-export of the shared slugify function with E2E-appropriate default maxLength.
*
* Rules:
* - allow Unicode letters/numbers (\p{L}\p{N}) and spaces, hyphen, underscore
* - remove dots completely (to avoid trailing-dot issues on Windows)
* - replace any other char with '-' (this includes brackets, quotes, parentheses, punctuation)
* - collapse multiple '-' into one, collapse multiple spaces into one, trim, and limit length
*/
const unsafeChars = /[^\p{L}\p{N}\s\-_]/gu;
const collapseDashes = /-+/g;
const collapseSpaces = /\s+/g;
export const makeSlugPath = (input: string | undefined, maxLength = 120) => {
let s = (input || 'unknown').normalize('NFKC');
// remove dots explicitly
s = s.replace(/\./g, '');
// replace unsafe characters with dashes
let slug = s.replace(unsafeChars, '-');
// collapse consecutive dashes
slug = slug.replace(collapseDashes, '-');
// collapse spaces to single space, trim edges
slug = slug.replace(collapseSpaces, ' ').trim();
// trim leading/trailing dashes or spaces
slug = slug.replace(/^-+|-+$/g, '').replace(/^[\s]+|[\s]+$/g, '');
if (slug.length > maxLength) slug = slug.substring(0, maxLength).trim();
// Final cleanup: remove trailing dashes/spaces that may appear after truncation
slug = slug.replace(/[-\s]+$/g, '');
if (!slug) slug = 'unknown';
return slug;
};
/**
* Get base path for test artifacts for a specific scenario
* This is the foundation for all scenario-specific paths
*/
export function getTestArtifactsPath(world: ApplicationWorld, ...subpaths: string[]): string {
return path.resolve(process.cwd(), 'test-artifacts', world.scenarioSlug, ...subpaths);
}
/**
* Get path to settings.json for a scenario
*/
export function getSettingsPath(world: ApplicationWorld): string {
return getTestArtifactsPath(world, 'userData-test', 'settings', 'settings.json');
}
/**
* Get path to wiki-test root folder for a scenario
*/
export function getWikiTestRootPath(world: ApplicationWorld): string {
return getTestArtifactsPath(world, 'wiki-test');
}
/**
* Get path to the main wiki folder (wiki-test/wiki) for a scenario
*/
export function getWikiTestWikiPath(world: ApplicationWorld): string {
return getTestArtifactsPath(world, 'wiki-test', 'wiki');
}
/**
* Get path to logs folder for a scenario
*/
export function getLogPath(world: ApplicationWorld): string {
return getTestArtifactsPath(world, 'userData-test', 'logs');
}