add-e2e-coverage (#364)
* takt: add-e2e-coverage * takt: add-e2e-coverage
This commit is contained in:
parent
e5902b87ad
commit
4ee69f857a
@ -1,5 +1,7 @@
|
|||||||
|
// E2E更新時は docs/testing/e2e.md も更新すること
|
||||||
|
|
||||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
import { existsSync, readFileSync } from 'node:fs';
|
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import { parse as parseYaml } from 'yaml';
|
import { parse as parseYaml } from 'yaml';
|
||||||
@ -34,20 +36,43 @@ function canAccessRepoRef(repo: string, ref: string): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fixtureHasManifest(repo: string, ref: string, filename: string): boolean {
|
||||||
|
try {
|
||||||
|
const out = execFileSync(
|
||||||
|
'gh',
|
||||||
|
['api', `/repos/${repo}/git/trees/${ref}`, '--recursive'],
|
||||||
|
{ encoding: 'utf-8', stdio: 'pipe' },
|
||||||
|
);
|
||||||
|
const tree = JSON.parse(out) as { tree: { path: string }[] };
|
||||||
|
return tree.tree.some((f) => f.path === filename);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function readYamlFile<T>(path: string): T {
|
function readYamlFile<T>(path: string): T {
|
||||||
const raw = readFileSync(path, 'utf-8');
|
const raw = readFileSync(path, 'utf-8');
|
||||||
return parseYaml(raw) as T;
|
return parseYaml(raw) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FIXTURE_REPO = 'nrslib/takt-ensemble-fixture';
|
const FIXTURE_REPO = 'nrslib/takt-repertoire-fixture';
|
||||||
const FIXTURE_REPO_SUBDIR = 'nrslib/takt-ensemble-fixture-subdir';
|
const FIXTURE_REPO_SUBDIR = 'nrslib/takt-repertoire-fixture-subdir';
|
||||||
const FIXTURE_REPO_FACETS_ONLY = 'nrslib/takt-ensemble-fixture-facets-only';
|
const FIXTURE_REPO_FACETS_ONLY = 'nrslib/takt-repertoire-fixture-facets-only';
|
||||||
const MISSING_MANIFEST_REPO = 'nrslib/takt';
|
const MISSING_MANIFEST_REPO = 'nrslib/takt';
|
||||||
const FIXTURE_REF = 'v1.0.0';
|
const FIXTURE_REF = 'v1.0.0';
|
||||||
|
|
||||||
const canUseFixtureRepo = canAccessRepo(FIXTURE_REPO) && canAccessRepoRef(FIXTURE_REPO, FIXTURE_REF);
|
const canUseFixtureRepo =
|
||||||
const canUseSubdirRepo = canAccessRepo(FIXTURE_REPO_SUBDIR) && canAccessRepoRef(FIXTURE_REPO_SUBDIR, FIXTURE_REF);
|
canAccessRepo(FIXTURE_REPO) &&
|
||||||
const canUseFacetsOnlyRepo = canAccessRepo(FIXTURE_REPO_FACETS_ONLY) && canAccessRepoRef(FIXTURE_REPO_FACETS_ONLY, FIXTURE_REF);
|
canAccessRepoRef(FIXTURE_REPO, FIXTURE_REF) &&
|
||||||
|
fixtureHasManifest(FIXTURE_REPO, FIXTURE_REF, 'takt-repertoire.yaml');
|
||||||
|
const canUseSubdirRepo =
|
||||||
|
canAccessRepo(FIXTURE_REPO_SUBDIR) &&
|
||||||
|
canAccessRepoRef(FIXTURE_REPO_SUBDIR, FIXTURE_REF) &&
|
||||||
|
fixtureHasManifest(FIXTURE_REPO_SUBDIR, FIXTURE_REF, 'takt-repertoire.yaml');
|
||||||
|
const canUseFacetsOnlyRepo =
|
||||||
|
canAccessRepo(FIXTURE_REPO_FACETS_ONLY) &&
|
||||||
|
canAccessRepoRef(FIXTURE_REPO_FACETS_ONLY, FIXTURE_REF) &&
|
||||||
|
fixtureHasManifest(FIXTURE_REPO_FACETS_ONLY, FIXTURE_REF, 'takt-repertoire.yaml');
|
||||||
const canUseMissingManifestRepo = canAccessRepo(MISSING_MANIFEST_REPO);
|
const canUseMissingManifestRepo = canAccessRepo(MISSING_MANIFEST_REPO);
|
||||||
|
|
||||||
describe('E2E: takt repertoire (real GitHub fixtures)', () => {
|
describe('E2E: takt repertoire (real GitHub fixtures)', () => {
|
||||||
@ -78,14 +103,14 @@ describe('E2E: takt repertoire (real GitHub fixtures)', () => {
|
|||||||
expect(result.stdout).toContain(`📦 ${FIXTURE_REPO} @${FIXTURE_REF}`);
|
expect(result.stdout).toContain(`📦 ${FIXTURE_REPO} @${FIXTURE_REF}`);
|
||||||
expect(result.stdout).toContain('インストールしました');
|
expect(result.stdout).toContain('インストールしました');
|
||||||
|
|
||||||
const packageDir = join(isolatedEnv.taktDir, 'repertoire', '@nrslib', 'takt-ensemble-fixture');
|
const packageDir = join(isolatedEnv.taktDir, 'repertoire', '@nrslib', 'takt-repertoire-fixture');
|
||||||
expect(existsSync(join(packageDir, 'takt-repertoire.yaml'))).toBe(true);
|
expect(existsSync(join(packageDir, 'takt-repertoire.yaml'))).toBe(true);
|
||||||
expect(existsSync(join(packageDir, '.takt-repertoire-lock.yaml'))).toBe(true);
|
expect(existsSync(join(packageDir, '.takt-repertoire-lock.yaml'))).toBe(true);
|
||||||
expect(existsSync(join(packageDir, 'facets'))).toBe(true);
|
expect(existsSync(join(packageDir, 'facets'))).toBe(true);
|
||||||
expect(existsSync(join(packageDir, 'pieces'))).toBe(true);
|
expect(existsSync(join(packageDir, 'pieces'))).toBe(true);
|
||||||
|
|
||||||
const lock = readYamlFile<LockFile>(join(packageDir, '.takt-repertoire-lock.yaml'));
|
const lock = readYamlFile<LockFile>(join(packageDir, '.takt-repertoire-lock.yaml'));
|
||||||
expect(lock.source).toBe('github:nrslib/takt-ensemble-fixture');
|
expect(lock.source).toBe('github:nrslib/takt-repertoire-fixture');
|
||||||
expect(lock.ref).toBe(FIXTURE_REF);
|
expect(lock.ref).toBe(FIXTURE_REF);
|
||||||
expect(lock.commit).toBeTypeOf('string');
|
expect(lock.commit).toBeTypeOf('string');
|
||||||
expect(lock.commit!.length).toBeGreaterThanOrEqual(7);
|
expect(lock.commit!.length).toBeGreaterThanOrEqual(7);
|
||||||
@ -110,7 +135,7 @@ describe('E2E: takt repertoire (real GitHub fixtures)', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(listResult.exitCode).toBe(0);
|
expect(listResult.exitCode).toBe(0);
|
||||||
expect(listResult.stdout).toContain('@nrslib/takt-ensemble-fixture');
|
expect(listResult.stdout).toContain('@nrslib/takt-repertoire-fixture');
|
||||||
}, 240_000);
|
}, 240_000);
|
||||||
|
|
||||||
it.skipIf(!canUseFixtureRepo)('should remove installed package with confirmation', () => {
|
it.skipIf(!canUseFixtureRepo)('should remove installed package with confirmation', () => {
|
||||||
@ -124,7 +149,7 @@ describe('E2E: takt repertoire (real GitHub fixtures)', () => {
|
|||||||
expect(addResult.exitCode).toBe(0);
|
expect(addResult.exitCode).toBe(0);
|
||||||
|
|
||||||
const removeResult = runTakt({
|
const removeResult = runTakt({
|
||||||
args: ['repertoire', 'remove', '@nrslib/takt-ensemble-fixture'],
|
args: ['repertoire', 'remove', '@nrslib/takt-repertoire-fixture'],
|
||||||
cwd: process.cwd(),
|
cwd: process.cwd(),
|
||||||
env: isolatedEnv.env,
|
env: isolatedEnv.env,
|
||||||
input: 'y\n',
|
input: 'y\n',
|
||||||
@ -132,7 +157,7 @@ describe('E2E: takt repertoire (real GitHub fixtures)', () => {
|
|||||||
});
|
});
|
||||||
expect(removeResult.exitCode).toBe(0);
|
expect(removeResult.exitCode).toBe(0);
|
||||||
|
|
||||||
const packageDir = join(isolatedEnv.taktDir, 'repertoire', '@nrslib', 'takt-ensemble-fixture');
|
const packageDir = join(isolatedEnv.taktDir, 'repertoire', '@nrslib', 'takt-repertoire-fixture');
|
||||||
expect(existsSync(packageDir)).toBe(false);
|
expect(existsSync(packageDir)).toBe(false);
|
||||||
}, 240_000);
|
}, 240_000);
|
||||||
|
|
||||||
@ -148,7 +173,7 @@ describe('E2E: takt repertoire (real GitHub fixtures)', () => {
|
|||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.stdout).toContain('キャンセルしました');
|
expect(result.stdout).toContain('キャンセルしました');
|
||||||
|
|
||||||
const packageDir = join(isolatedEnv.taktDir, 'repertoire', '@nrslib', 'takt-ensemble-fixture');
|
const packageDir = join(isolatedEnv.taktDir, 'repertoire', '@nrslib', 'takt-repertoire-fixture');
|
||||||
expect(existsSync(packageDir)).toBe(false);
|
expect(existsSync(packageDir)).toBe(false);
|
||||||
}, 240_000);
|
}, 240_000);
|
||||||
|
|
||||||
@ -162,7 +187,7 @@ describe('E2E: takt repertoire (real GitHub fixtures)', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
const packageDir = join(isolatedEnv.taktDir, 'repertoire', '@nrslib', 'takt-ensemble-fixture-subdir');
|
const packageDir = join(isolatedEnv.taktDir, 'repertoire', '@nrslib', 'takt-repertoire-fixture-subdir');
|
||||||
expect(existsSync(join(packageDir, 'takt-repertoire.yaml'))).toBe(true);
|
expect(existsSync(join(packageDir, 'takt-repertoire.yaml'))).toBe(true);
|
||||||
expect(existsSync(join(packageDir, '.takt-repertoire-lock.yaml'))).toBe(true);
|
expect(existsSync(join(packageDir, '.takt-repertoire-lock.yaml'))).toBe(true);
|
||||||
expect(existsSync(join(packageDir, 'facets')) || existsSync(join(packageDir, 'pieces'))).toBe(true);
|
expect(existsSync(join(packageDir, 'facets')) || existsSync(join(packageDir, 'pieces'))).toBe(true);
|
||||||
@ -178,7 +203,7 @@ describe('E2E: takt repertoire (real GitHub fixtures)', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
const packageDir = join(isolatedEnv.taktDir, 'repertoire', '@nrslib', 'takt-ensemble-fixture-facets-only');
|
const packageDir = join(isolatedEnv.taktDir, 'repertoire', '@nrslib', 'takt-repertoire-fixture-facets-only');
|
||||||
expect(existsSync(join(packageDir, 'facets'))).toBe(true);
|
expect(existsSync(join(packageDir, 'facets'))).toBe(true);
|
||||||
expect(existsSync(join(packageDir, 'pieces'))).toBe(false);
|
expect(existsSync(join(packageDir, 'pieces'))).toBe(false);
|
||||||
}, 240_000);
|
}, 240_000);
|
||||||
@ -195,4 +220,210 @@ describe('E2E: takt repertoire (real GitHub fixtures)', () => {
|
|||||||
expect(result.exitCode).not.toBe(0);
|
expect(result.exitCode).not.toBe(0);
|
||||||
expect(result.stdout).toContain('takt-repertoire.yaml not found');
|
expect(result.stdout).toContain('takt-repertoire.yaml not found');
|
||||||
}, 240_000);
|
}, 240_000);
|
||||||
|
|
||||||
|
it.skipIf(!canUseFixtureRepo)(
|
||||||
|
'should display pre-install summary with package name, faceted info, and pieces list',
|
||||||
|
() => {
|
||||||
|
const result = runTakt({
|
||||||
|
args: ['repertoire', 'add', `github:${FIXTURE_REPO}@${FIXTURE_REF}`],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'n\n',
|
||||||
|
timeout: 240_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.stdout).toContain(`nrslib/takt-repertoire-fixture @${FIXTURE_REF}`);
|
||||||
|
expect(result.stdout).toContain('facets:');
|
||||||
|
expect(result.stdout).toContain('pieces:');
|
||||||
|
expect(result.stdout).toContain('キャンセルしました');
|
||||||
|
},
|
||||||
|
240_000,
|
||||||
|
);
|
||||||
|
|
||||||
|
it.skipIf(!canUseFixtureRepo)(
|
||||||
|
'should display already-installed warning when adding a package that is already installed',
|
||||||
|
() => {
|
||||||
|
const firstResult = runTakt({
|
||||||
|
args: ['repertoire', 'add', `github:${FIXTURE_REPO}@${FIXTURE_REF}`],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'y\n',
|
||||||
|
timeout: 240_000,
|
||||||
|
});
|
||||||
|
expect(firstResult.exitCode).toBe(0);
|
||||||
|
|
||||||
|
const secondResult = runTakt({
|
||||||
|
args: ['repertoire', 'add', `github:${FIXTURE_REPO}@${FIXTURE_REF}`],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'y\nn\n',
|
||||||
|
timeout: 240_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(secondResult.exitCode).toBe(0);
|
||||||
|
expect(secondResult.stdout).toContain('既にインストールされています');
|
||||||
|
},
|
||||||
|
240_000,
|
||||||
|
);
|
||||||
|
|
||||||
|
it.skipIf(!canUseFixtureRepo)(
|
||||||
|
'should atomically update package with no leftover tmp or bak directories when user answers y to overwrite',
|
||||||
|
() => {
|
||||||
|
const firstResult = runTakt({
|
||||||
|
args: ['repertoire', 'add', `github:${FIXTURE_REPO}@${FIXTURE_REF}`],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'y\n',
|
||||||
|
timeout: 240_000,
|
||||||
|
});
|
||||||
|
expect(firstResult.exitCode).toBe(0);
|
||||||
|
|
||||||
|
const packageDir = join(
|
||||||
|
isolatedEnv.taktDir,
|
||||||
|
'repertoire',
|
||||||
|
'@nrslib',
|
||||||
|
'takt-repertoire-fixture',
|
||||||
|
);
|
||||||
|
|
||||||
|
const secondResult = runTakt({
|
||||||
|
args: ['repertoire', 'add', `github:${FIXTURE_REPO}@${FIXTURE_REF}`],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'y\ny\n',
|
||||||
|
timeout: 240_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(secondResult.exitCode).toBe(0);
|
||||||
|
expect(existsSync(`${packageDir}.tmp`)).toBe(false);
|
||||||
|
expect(existsSync(`${packageDir}.bak`)).toBe(false);
|
||||||
|
expect(existsSync(join(packageDir, '.takt-repertoire-lock.yaml'))).toBe(true);
|
||||||
|
},
|
||||||
|
240_000,
|
||||||
|
);
|
||||||
|
|
||||||
|
it.skipIf(!canUseFixtureRepo)(
|
||||||
|
'should keep existing package unchanged when user answers N to overwrite prompt',
|
||||||
|
() => {
|
||||||
|
const firstResult = runTakt({
|
||||||
|
args: ['repertoire', 'add', `github:${FIXTURE_REPO}@${FIXTURE_REF}`],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'y\n',
|
||||||
|
timeout: 240_000,
|
||||||
|
});
|
||||||
|
expect(firstResult.exitCode).toBe(0);
|
||||||
|
|
||||||
|
const lockPath = join(
|
||||||
|
isolatedEnv.taktDir,
|
||||||
|
'repertoire',
|
||||||
|
'@nrslib',
|
||||||
|
'takt-repertoire-fixture',
|
||||||
|
'.takt-repertoire-lock.yaml',
|
||||||
|
);
|
||||||
|
const originalLock = readYamlFile<LockFile>(lockPath);
|
||||||
|
|
||||||
|
const secondResult = runTakt({
|
||||||
|
args: ['repertoire', 'add', `github:${FIXTURE_REPO}@${FIXTURE_REF}`],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'y\nn\n',
|
||||||
|
timeout: 240_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(secondResult.exitCode).toBe(0);
|
||||||
|
const afterLock = readYamlFile<LockFile>(lockPath);
|
||||||
|
expect(afterLock.commit).toBe(originalLock.commit);
|
||||||
|
expect(afterLock.imported_at).toBe(originalLock.imported_at);
|
||||||
|
},
|
||||||
|
240_000,
|
||||||
|
);
|
||||||
|
|
||||||
|
it.skipIf(!canUseFixtureRepo)(
|
||||||
|
'should clean up leftover .tmp/ directory from previous failed installation and succeed',
|
||||||
|
() => {
|
||||||
|
const firstResult = runTakt({
|
||||||
|
args: ['repertoire', 'add', `github:${FIXTURE_REPO}@${FIXTURE_REF}`],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'y\n',
|
||||||
|
timeout: 240_000,
|
||||||
|
});
|
||||||
|
expect(firstResult.exitCode).toBe(0);
|
||||||
|
|
||||||
|
const packageDir = join(
|
||||||
|
isolatedEnv.taktDir,
|
||||||
|
'repertoire',
|
||||||
|
'@nrslib',
|
||||||
|
'takt-repertoire-fixture',
|
||||||
|
);
|
||||||
|
|
||||||
|
mkdirSync(`${packageDir}.tmp`, { recursive: true });
|
||||||
|
|
||||||
|
const secondResult = runTakt({
|
||||||
|
args: ['repertoire', 'add', `github:${FIXTURE_REPO}@${FIXTURE_REF}`],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'y\ny\n',
|
||||||
|
timeout: 240_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(secondResult.exitCode).toBe(0);
|
||||||
|
expect(existsSync(`${packageDir}.tmp`)).toBe(false);
|
||||||
|
},
|
||||||
|
240_000,
|
||||||
|
);
|
||||||
|
|
||||||
|
it.skipIf(!canUseFixtureRepo)(
|
||||||
|
'should clean up leftover .bak/ directory from previous failed installation and succeed',
|
||||||
|
() => {
|
||||||
|
const firstResult = runTakt({
|
||||||
|
args: ['repertoire', 'add', `github:${FIXTURE_REPO}@${FIXTURE_REF}`],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'y\n',
|
||||||
|
timeout: 240_000,
|
||||||
|
});
|
||||||
|
expect(firstResult.exitCode).toBe(0);
|
||||||
|
|
||||||
|
const packageDir = join(
|
||||||
|
isolatedEnv.taktDir,
|
||||||
|
'repertoire',
|
||||||
|
'@nrslib',
|
||||||
|
'takt-repertoire-fixture',
|
||||||
|
);
|
||||||
|
|
||||||
|
mkdirSync(`${packageDir}.bak`, { recursive: true });
|
||||||
|
|
||||||
|
const secondResult = runTakt({
|
||||||
|
args: ['repertoire', 'add', `github:${FIXTURE_REPO}@${FIXTURE_REF}`],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'y\ny\n',
|
||||||
|
timeout: 240_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(secondResult.exitCode).toBe(0);
|
||||||
|
expect(existsSync(`${packageDir}.bak`)).toBe(false);
|
||||||
|
},
|
||||||
|
240_000,
|
||||||
|
);
|
||||||
|
|
||||||
|
it.todo('should populate lock file commit field with the specified commit SHA when installing by SHA');
|
||||||
|
|
||||||
|
it.todo('should display warning symbol when package contains piece with edit: true');
|
||||||
|
|
||||||
|
it.todo('should reject takt-repertoire.yaml with absolute path in path field (/foo)');
|
||||||
|
|
||||||
|
it.todo('should reject takt-repertoire.yaml with path traversal via ".." segments');
|
||||||
|
|
||||||
|
it.todo('should reject package with neither facets/ nor pieces/ directory');
|
||||||
|
|
||||||
|
it.todo('should reject takt-repertoire.yaml with min_version "1.0" (missing patch segment)');
|
||||||
|
|
||||||
|
it.todo('should reject takt-repertoire.yaml with min_version "v1.0.0" (v prefix)');
|
||||||
|
|
||||||
|
it.todo('should reject takt-repertoire.yaml with min_version "1.0.0-alpha" (pre-release suffix)');
|
||||||
|
|
||||||
|
it.todo('should fail with version mismatch message when min_version exceeds current takt version');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,71 +1,63 @@
|
|||||||
/**
|
// E2E更新時は docs/testing/e2e.md も更新すること
|
||||||
* E2E tests for `takt repertoire` subcommands.
|
|
||||||
*
|
|
||||||
* All tests are marked as `it.todo()` because the `takt repertoire` command
|
|
||||||
* is not yet implemented. These serve as the specification skeleton;
|
|
||||||
* fill in the callbacks when the implementation lands.
|
|
||||||
*
|
|
||||||
* GitHub fixture repos used:
|
|
||||||
* - github:nrslib/takt-ensemble-fixture (standard: facets/ + pieces/)
|
|
||||||
* - github:nrslib/takt-ensemble-fixture-subdir (path field specified)
|
|
||||||
* - github:nrslib/takt-ensemble-fixture-facets-only (facets only, no pieces/)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { describe, it } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { stringify as stringifyYaml } from 'yaml';
|
||||||
|
import { createIsolatedEnv, type IsolatedEnv } from '../helpers/isolated-env';
|
||||||
|
import { runTakt } from '../helpers/takt-runner';
|
||||||
|
|
||||||
|
interface FakePackageOptions {
|
||||||
|
taktDir: string;
|
||||||
|
owner: string;
|
||||||
|
repo: string;
|
||||||
|
description?: string;
|
||||||
|
ref?: string;
|
||||||
|
commit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFakePackage(opts: FakePackageOptions): string {
|
||||||
|
const { taktDir, owner, repo } = opts;
|
||||||
|
const ref = opts.ref ?? 'v1.0.0';
|
||||||
|
const commit = opts.commit ?? 'abc1234def5678';
|
||||||
|
const packageDir = join(taktDir, 'repertoire', `@${owner}`, repo);
|
||||||
|
|
||||||
|
mkdirSync(packageDir, { recursive: true });
|
||||||
|
|
||||||
|
const manifest: Record<string, unknown> = { path: '.' };
|
||||||
|
if (opts.description) manifest.description = opts.description;
|
||||||
|
writeFileSync(join(packageDir, 'takt-repertoire.yaml'), stringifyYaml(manifest));
|
||||||
|
|
||||||
|
const lock = {
|
||||||
|
source: `github:${owner}/${repo}`,
|
||||||
|
ref,
|
||||||
|
commit,
|
||||||
|
imported_at: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
writeFileSync(join(packageDir, '.takt-repertoire-lock.yaml'), stringifyYaml(lock));
|
||||||
|
|
||||||
|
return packageDir;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// E2E: takt repertoire add — 正常系
|
// E2E: takt repertoire add — 正常系
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
describe('E2E: takt repertoire add (正常系)', () => {
|
describe('E2E: takt repertoire add (正常系)', () => {
|
||||||
// E1: 標準パッケージのインポート
|
|
||||||
// Given: 空の isolatedEnv
|
|
||||||
// When: takt repertoire add github:nrslib/takt-ensemble-fixture@v1.0.0、y 入力
|
|
||||||
// Then: {taktDir}/repertoire/@nrslib/takt-ensemble-fixture/ に takt-repertoire.yaml,
|
|
||||||
// .takt-repertoire-lock.yaml, facets/, pieces/ が存在する
|
|
||||||
it.todo('should install standard package and verify directory structure');
|
it.todo('should install standard package and verify directory structure');
|
||||||
|
|
||||||
// E2: lock ファイルのフィールド確認
|
|
||||||
// Given: E1 完了後
|
|
||||||
// When: .takt-repertoire-lock.yaml を読む
|
|
||||||
// Then: source, ref, commit, imported_at フィールドがすべて存在する
|
|
||||||
it.todo('should generate .takt-repertoire-lock.yaml with source, ref, commit, imported_at');
|
it.todo('should generate .takt-repertoire-lock.yaml with source, ref, commit, imported_at');
|
||||||
|
|
||||||
// E3: サブディレクトリ型パッケージのインポート
|
|
||||||
// Given: 空の isolatedEnv
|
|
||||||
// When: takt repertoire add github:nrslib/takt-ensemble-fixture-subdir@v1.0.0、y 入力
|
|
||||||
// Then: path フィールドで指定されたサブディレクトリ配下のファイルのみコピーされる
|
|
||||||
it.todo('should install subdir-type package and copy only path-specified files');
|
it.todo('should install subdir-type package and copy only path-specified files');
|
||||||
|
|
||||||
// E4: ファセットのみパッケージのインポート
|
|
||||||
// Given: 空の isolatedEnv
|
|
||||||
// When: takt repertoire add github:nrslib/takt-ensemble-fixture-facets-only@v1.0.0、y 入力
|
|
||||||
// Then: facets/ は存在し、pieces/ ディレクトリは存在しない
|
|
||||||
it.todo('should install facets-only package without creating pieces/ directory');
|
it.todo('should install facets-only package without creating pieces/ directory');
|
||||||
|
|
||||||
// E4b: コミットSHA指定
|
|
||||||
// Given: 空の isolatedEnv
|
|
||||||
// When: takt repertoire add github:nrslib/takt-ensemble-fixture@{sha}、y 入力
|
|
||||||
// Then: .takt-repertoire-lock.yaml の commit フィールドが指定した SHA と一致する
|
|
||||||
it.todo('should populate lock file commit field with the specified commit SHA when installing by SHA');
|
it.todo('should populate lock file commit field with the specified commit SHA when installing by SHA');
|
||||||
|
|
||||||
// E5: インストール前サマリー表示
|
|
||||||
// Given: 空の isolatedEnv
|
|
||||||
// When: takt repertoire add github:nrslib/takt-ensemble-fixture@v1.0.0、N 入力(確認でキャンセル)
|
|
||||||
// Then: stdout に "📦 nrslib/takt-ensemble-fixture", "faceted:", "pieces:" が含まれる
|
|
||||||
it.todo('should display pre-install summary with package name, faceted count, and pieces list');
|
it.todo('should display pre-install summary with package name, faceted count, and pieces list');
|
||||||
|
|
||||||
// E6: 権限警告表示(edit: true ピース)
|
|
||||||
// Given: edit: true を含むパッケージ
|
|
||||||
// When: repertoire add、N 入力
|
|
||||||
// Then: stdout に ⚠ が含まれる
|
|
||||||
it.todo('should display warning symbol when package contains piece with edit: true');
|
it.todo('should display warning symbol when package contains piece with edit: true');
|
||||||
|
|
||||||
// E7: ユーザー確認 N で中断
|
|
||||||
// Given: 空の isolatedEnv
|
|
||||||
// When: repertoire add、N 入力
|
|
||||||
// Then: インストールディレクトリが存在しない。exit code 0
|
|
||||||
it.todo('should abort installation when user answers N to confirmation prompt');
|
it.todo('should abort installation when user answers N to confirmation prompt');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -74,34 +66,14 @@ describe('E2E: takt repertoire add (正常系)', () => {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
describe('E2E: takt repertoire add (上書きシナリオ)', () => {
|
describe('E2E: takt repertoire add (上書きシナリオ)', () => {
|
||||||
// E8: 既存パッケージの上書き警告表示
|
|
||||||
// Given: 1回目インストール済み
|
|
||||||
// When: 2回目 repertoire add
|
|
||||||
// Then: stdout に "⚠ パッケージ @nrslib/takt-ensemble-fixture は既にインストールされています" が含まれる
|
|
||||||
it.todo('should display already-installed warning on second add');
|
it.todo('should display already-installed warning on second add');
|
||||||
|
|
||||||
// E9: 上書き y で原子的更新
|
|
||||||
// Given: E8後、y 入力
|
|
||||||
// When: インストール完了後
|
|
||||||
// Then: .tmp/, .bak/ が残っていない。新 lock ファイルが配置済み
|
|
||||||
it.todo('should atomically update package when user answers y to overwrite prompt');
|
it.todo('should atomically update package when user answers y to overwrite prompt');
|
||||||
|
|
||||||
// E10: 上書き N でキャンセル
|
|
||||||
// Given: E8後、N 入力
|
|
||||||
// When: コマンド終了後
|
|
||||||
// Then: 既存パッケージが維持される(元 lock ファイルが変わらない)
|
|
||||||
it.todo('should keep existing package when user answers N to overwrite prompt');
|
it.todo('should keep existing package when user answers N to overwrite prompt');
|
||||||
|
|
||||||
// E11: 前回異常終了残留物(.tmp/)クリーンアップ
|
|
||||||
// Given: {repertoireDir}/@nrslib/takt-ensemble-fixture.tmp/ が既に存在する状態
|
|
||||||
// When: repertoire add、y 入力
|
|
||||||
// Then: インストールが正常完了する。exit code 0
|
|
||||||
it.todo('should clean up leftover .tmp/ directory from previous failed installation');
|
it.todo('should clean up leftover .tmp/ directory from previous failed installation');
|
||||||
|
|
||||||
// E12: 前回異常終了残留物(.bak/)クリーンアップ
|
|
||||||
// Given: {repertoireDir}/@nrslib/takt-ensemble-fixture.bak/ が既に存在する状態
|
|
||||||
// When: repertoire add、y 入力
|
|
||||||
// Then: インストールが正常完了する。exit code 0
|
|
||||||
it.todo('should clean up leftover .bak/ directory from previous failed installation');
|
it.todo('should clean up leftover .bak/ directory from previous failed installation');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -110,112 +82,226 @@ describe('E2E: takt repertoire add (上書きシナリオ)', () => {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
describe('E2E: takt repertoire add (バリデーション・エラー系)', () => {
|
describe('E2E: takt repertoire add (バリデーション・エラー系)', () => {
|
||||||
// E13: takt-repertoire.yaml 不在リポジトリ
|
|
||||||
// Given: takt-repertoire.yaml のないリポジトリを指定
|
|
||||||
// When: repertoire add
|
|
||||||
// Then: exit code 非0。エラーメッセージ表示
|
|
||||||
it.todo('should fail with error when repository has no takt-repertoire.yaml');
|
it.todo('should fail with error when repository has no takt-repertoire.yaml');
|
||||||
|
|
||||||
// E14: path に絶対パス(/foo)
|
|
||||||
// Given: path: /foo の takt-repertoire.yaml
|
|
||||||
// When: repertoire add
|
|
||||||
// Then: exit code 非0
|
|
||||||
it.todo('should reject takt-repertoire.yaml with absolute path in path field (/foo)');
|
it.todo('should reject takt-repertoire.yaml with absolute path in path field (/foo)');
|
||||||
|
|
||||||
// E15: path に .. によるリポジトリ外参照
|
|
||||||
// Given: path: ../outside の takt-repertoire.yaml
|
|
||||||
// When: repertoire add
|
|
||||||
// Then: exit code 非0
|
|
||||||
it.todo('should reject takt-repertoire.yaml with path traversal via ".." segments');
|
it.todo('should reject takt-repertoire.yaml with path traversal via ".." segments');
|
||||||
|
|
||||||
// E16: 空パッケージ(facets/ も pieces/ もない)
|
|
||||||
// Given: facets/, pieces/ のどちらもない takt-repertoire.yaml
|
|
||||||
// When: repertoire add
|
|
||||||
// Then: exit code 非0
|
|
||||||
it.todo('should reject package with neither facets/ nor pieces/ directory');
|
it.todo('should reject package with neither facets/ nor pieces/ directory');
|
||||||
|
|
||||||
// E17: min_version 不正形式(1.0、セグメント不足)
|
|
||||||
// Given: takt.min_version: "1.0"
|
|
||||||
// When: repertoire add
|
|
||||||
// Then: exit code 非0
|
|
||||||
it.todo('should reject takt-repertoire.yaml with min_version "1.0" (missing patch segment)');
|
it.todo('should reject takt-repertoire.yaml with min_version "1.0" (missing patch segment)');
|
||||||
|
|
||||||
// E18: min_version 不正形式(v1.0.0、v プレフィックス)
|
|
||||||
// Given: takt.min_version: "v1.0.0"
|
|
||||||
// When: repertoire add
|
|
||||||
// Then: exit code 非0
|
|
||||||
it.todo('should reject takt-repertoire.yaml with min_version "v1.0.0" (v prefix)');
|
it.todo('should reject takt-repertoire.yaml with min_version "v1.0.0" (v prefix)');
|
||||||
|
|
||||||
// E19: min_version 不正形式(1.0.0-alpha、pre-release)
|
|
||||||
// Given: takt.min_version: "1.0.0-alpha"
|
|
||||||
// When: repertoire add
|
|
||||||
// Then: exit code 非0
|
|
||||||
it.todo('should reject takt-repertoire.yaml with min_version "1.0.0-alpha" (pre-release suffix)');
|
it.todo('should reject takt-repertoire.yaml with min_version "1.0.0-alpha" (pre-release suffix)');
|
||||||
|
|
||||||
// E20: min_version が現在の TAKT より新しい
|
|
||||||
// Given: takt.min_version: "999.0.0"
|
|
||||||
// When: repertoire add
|
|
||||||
// Then: exit code 非0。必要バージョンと現在バージョンが表示される
|
|
||||||
it.todo('should fail with version mismatch message when min_version exceeds current takt version');
|
it.todo('should fail with version mismatch message when min_version exceeds current takt version');
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// E2E: takt repertoire remove
|
// E2E: takt repertoire remove (mock)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
describe('E2E: takt repertoire remove', () => {
|
describe('E2E: takt repertoire remove (mock)', () => {
|
||||||
// E21: 正常削除 y
|
let isolatedEnv: IsolatedEnv;
|
||||||
// Given: パッケージインストール済み
|
|
||||||
// When: takt repertoire remove @nrslib/takt-ensemble-fixture、y 入力
|
|
||||||
// Then: ディレクトリが削除される。@nrslib/ 配下が空なら @nrslib/ も削除
|
|
||||||
it.todo('should remove installed package directory when user answers y');
|
|
||||||
|
|
||||||
// E22: owner dir 残存(他パッケージがある場合)
|
beforeEach(() => {
|
||||||
// Given: @nrslib 配下に別パッケージもインストール済み
|
isolatedEnv = createIsolatedEnv();
|
||||||
// When: remove、y 入力
|
});
|
||||||
// Then: 対象パッケージのみ削除。@nrslib/ は残る
|
|
||||||
it.todo('should keep @scope directory when other packages remain under same owner');
|
|
||||||
|
|
||||||
// E23: 参照ありでの警告付き削除
|
afterEach(() => {
|
||||||
// Given: ~/.takt/pieces/ に @scope 参照するファイルあり
|
try {
|
||||||
// When: remove、y 入力
|
isolatedEnv.cleanup();
|
||||||
// Then: 警告("⚠ 次のファイルが...を参照しています")が表示され、削除は実行される
|
} catch {
|
||||||
it.todo('should display reference warning before deletion but still proceed when user answers y');
|
// best-effort
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// E24: 参照ファイル自体は変更されない
|
it('should remove installed package and delete empty owner directory when user answers y', () => {
|
||||||
// Given: E23後
|
const scope = '@testowner/single-fixture';
|
||||||
// When: 参照ファイルを読む
|
createFakePackage({ taktDir: isolatedEnv.taktDir, owner: 'testowner', repo: 'single-fixture' });
|
||||||
// Then: 元の @scope 参照がそのまま残っている
|
|
||||||
it.todo('should not modify reference files during removal');
|
|
||||||
|
|
||||||
// E25: 削除キャンセル N
|
const result = runTakt({
|
||||||
// Given: パッケージインストール済み
|
args: ['repertoire', 'remove', scope],
|
||||||
// When: remove、N 入力
|
cwd: process.cwd(),
|
||||||
// Then: ディレクトリが残る。exit code 0
|
env: isolatedEnv.env,
|
||||||
it.todo('should keep package directory when user answers N to removal prompt');
|
input: 'y\n',
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
const packageDir = join(isolatedEnv.taktDir, 'repertoire', '@testowner', 'single-fixture');
|
||||||
|
const ownerDir = join(isolatedEnv.taktDir, 'repertoire', '@testowner');
|
||||||
|
expect(existsSync(packageDir)).toBe(false);
|
||||||
|
expect(existsSync(ownerDir)).toBe(false);
|
||||||
|
}, 240_000);
|
||||||
|
|
||||||
|
it('should keep owner directory when other packages remain under same scope', () => {
|
||||||
|
createFakePackage({ taktDir: isolatedEnv.taktDir, owner: 'testowner', repo: 'fixture-a' });
|
||||||
|
createFakePackage({ taktDir: isolatedEnv.taktDir, owner: 'testowner', repo: 'fixture-b' });
|
||||||
|
|
||||||
|
const result = runTakt({
|
||||||
|
args: ['repertoire', 'remove', '@testowner/fixture-a'],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'y\n',
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
const removedDir = join(isolatedEnv.taktDir, 'repertoire', '@testowner', 'fixture-a');
|
||||||
|
const ownerDir = join(isolatedEnv.taktDir, 'repertoire', '@testowner');
|
||||||
|
const remainingDir = join(isolatedEnv.taktDir, 'repertoire', '@testowner', 'fixture-b');
|
||||||
|
expect(existsSync(removedDir)).toBe(false);
|
||||||
|
expect(existsSync(ownerDir)).toBe(true);
|
||||||
|
expect(existsSync(remainingDir)).toBe(true);
|
||||||
|
}, 240_000);
|
||||||
|
|
||||||
|
it('should display reference warning before deletion but still proceed when user answers y', () => {
|
||||||
|
const scope = '@testowner/ref-fixture';
|
||||||
|
createFakePackage({ taktDir: isolatedEnv.taktDir, owner: 'testowner', repo: 'ref-fixture' });
|
||||||
|
|
||||||
|
const piecesDir = join(isolatedEnv.taktDir, 'pieces');
|
||||||
|
mkdirSync(piecesDir, { recursive: true });
|
||||||
|
writeFileSync(join(piecesDir, 'ref-piece.yaml'), `from: ${scope}\nname: example\n`);
|
||||||
|
|
||||||
|
const result = runTakt({
|
||||||
|
args: ['repertoire', 'remove', scope],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'y\n',
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.stdout).toContain('⚠ 以下のファイルが');
|
||||||
|
expect(result.stdout).toContain('を参照しています');
|
||||||
|
const packageDir = join(isolatedEnv.taktDir, 'repertoire', '@testowner', 'ref-fixture');
|
||||||
|
expect(existsSync(packageDir)).toBe(false);
|
||||||
|
}, 240_000);
|
||||||
|
|
||||||
|
it('should not modify reference files during package removal', () => {
|
||||||
|
const scope = '@testowner/ref-fixture2';
|
||||||
|
createFakePackage({ taktDir: isolatedEnv.taktDir, owner: 'testowner', repo: 'ref-fixture2' });
|
||||||
|
|
||||||
|
const piecesDir = join(isolatedEnv.taktDir, 'pieces');
|
||||||
|
mkdirSync(piecesDir, { recursive: true });
|
||||||
|
const refFilePath = join(piecesDir, 'ref-piece2.yaml');
|
||||||
|
const originalContent = `from: ${scope}\nname: example\n`;
|
||||||
|
writeFileSync(refFilePath, originalContent);
|
||||||
|
|
||||||
|
const removeResult = runTakt({
|
||||||
|
args: ['repertoire', 'remove', scope],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'y\n',
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
expect(removeResult.exitCode).toBe(0);
|
||||||
|
|
||||||
|
const afterContent = readFileSync(refFilePath, 'utf-8');
|
||||||
|
expect(afterContent).toBe(originalContent);
|
||||||
|
}, 240_000);
|
||||||
|
|
||||||
|
it('should keep package directory when user answers N to removal prompt', () => {
|
||||||
|
const scope = '@testowner/keep-fixture';
|
||||||
|
createFakePackage({ taktDir: isolatedEnv.taktDir, owner: 'testowner', repo: 'keep-fixture' });
|
||||||
|
|
||||||
|
const result = runTakt({
|
||||||
|
args: ['repertoire', 'remove', scope],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
input: 'n\n',
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.stdout).toContain('キャンセルしました');
|
||||||
|
const packageDir = join(isolatedEnv.taktDir, 'repertoire', '@testowner', 'keep-fixture');
|
||||||
|
expect(existsSync(packageDir)).toBe(true);
|
||||||
|
}, 240_000);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// E2E: takt repertoire list
|
// E2E: takt repertoire list (mock)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
describe('E2E: takt repertoire list', () => {
|
describe('E2E: takt repertoire list (mock)', () => {
|
||||||
// E26: インストール済みパッケージ一覧表示
|
let isolatedEnv: IsolatedEnv;
|
||||||
// Given: パッケージ1件インストール済み
|
|
||||||
// When: takt repertoire list
|
|
||||||
// Then: "📦 インストール済みパッケージ:" と @nrslib/takt-ensemble-fixture、
|
|
||||||
// description、ref、commit 先頭7文字が表示される
|
|
||||||
it.todo('should list installed packages with name, description, ref, and abbreviated commit');
|
|
||||||
|
|
||||||
// E27: 空状態での表示
|
beforeEach(() => {
|
||||||
// Given: repertoire/ が空(パッケージなし)
|
isolatedEnv = createIsolatedEnv();
|
||||||
// When: takt repertoire list
|
});
|
||||||
// Then: パッケージなし相当のメッセージ。exit code 0
|
|
||||||
it.todo('should display empty-state message when no packages are installed');
|
|
||||||
|
|
||||||
// E28: 複数パッケージの一覧
|
afterEach(() => {
|
||||||
// Given: 2件以上インストール済み
|
try {
|
||||||
// When: takt repertoire list
|
isolatedEnv.cleanup();
|
||||||
// Then: すべてのパッケージが表示される
|
} catch {
|
||||||
it.todo('should list all installed packages when multiple packages exist');
|
// best-effort
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should list installed package with name, description, ref, and abbreviated commit', () => {
|
||||||
|
createFakePackage({
|
||||||
|
taktDir: isolatedEnv.taktDir,
|
||||||
|
owner: 'testowner',
|
||||||
|
repo: 'list-fixture',
|
||||||
|
description: 'My test package',
|
||||||
|
ref: 'v2.0.0',
|
||||||
|
commit: 'abcdef1234567',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = runTakt({
|
||||||
|
args: ['repertoire', 'list'],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.stdout).toContain('📦 インストール済みパッケージ:');
|
||||||
|
expect(result.stdout).toContain('@testowner/list-fixture');
|
||||||
|
expect(result.stdout).toContain('My test package');
|
||||||
|
expect(result.stdout).toContain('v2.0.0');
|
||||||
|
expect(result.stdout).toContain('abcdef1');
|
||||||
|
}, 240_000);
|
||||||
|
|
||||||
|
it('should display empty-state message when no packages are installed', () => {
|
||||||
|
const result = runTakt({
|
||||||
|
args: ['repertoire', 'list'],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.stdout).toContain('インストール済みパッケージはありません');
|
||||||
|
}, 240_000);
|
||||||
|
|
||||||
|
it('should list all installed packages when multiple packages exist', () => {
|
||||||
|
createFakePackage({
|
||||||
|
taktDir: isolatedEnv.taktDir,
|
||||||
|
owner: 'ownerA',
|
||||||
|
repo: 'pkg-alpha',
|
||||||
|
description: 'Alpha package',
|
||||||
|
});
|
||||||
|
createFakePackage({
|
||||||
|
taktDir: isolatedEnv.taktDir,
|
||||||
|
owner: 'ownerB',
|
||||||
|
repo: 'pkg-beta',
|
||||||
|
description: 'Beta package',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = runTakt({
|
||||||
|
args: ['repertoire', 'list'],
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: isolatedEnv.env,
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.stdout).toContain('@ownerA/pkg-alpha');
|
||||||
|
expect(result.stdout).toContain('@ownerB/pkg-beta');
|
||||||
|
}, 240_000);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -36,8 +36,8 @@ export default defineConfig({
|
|||||||
'e2e/specs/quiet-mode.e2e.ts',
|
'e2e/specs/quiet-mode.e2e.ts',
|
||||||
'e2e/specs/task-content-file.e2e.ts',
|
'e2e/specs/task-content-file.e2e.ts',
|
||||||
'e2e/specs/config-priority.e2e.ts',
|
'e2e/specs/config-priority.e2e.ts',
|
||||||
'e2e/specs/ensemble.e2e.ts',
|
'e2e/specs/repertoire.e2e.ts',
|
||||||
'e2e/specs/ensemble-real.e2e.ts',
|
'e2e/specs/repertoire-real.e2e.ts',
|
||||||
'e2e/specs/piece-selection-branches.e2e.ts',
|
'e2e/specs/piece-selection-branches.e2e.ts',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user