mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2026-03-09 08:20:32 -07:00
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).
117 lines
4.1 KiB
TypeScript
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');
|
|
}
|