111 lines
6.0 KiB
TypeScript
111 lines
6.0 KiB
TypeScript
import { cp, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
import { join } from 'node:path';
|
|
import { tmpdir } from 'node:os';
|
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
import { parse as parseYaml } from 'yaml';
|
|
import {
|
|
initializeWorksPackageTemplate,
|
|
resolveWorksPackageTemplateDirectory,
|
|
} from '@electron/opencode/project-package-template';
|
|
|
|
const temporaryDirectories: string[] = [];
|
|
const resourcesDirectory = join(process.cwd(), 'resources');
|
|
|
|
async function temporaryDirectory(prefix: string): Promise<string> {
|
|
const directory = await mkdtemp(join(tmpdir(), prefix));
|
|
temporaryDirectories.push(directory);
|
|
return directory;
|
|
}
|
|
|
|
afterEach(async () => {
|
|
await Promise.all(temporaryDirectories.splice(0).map((directory) => rm(directory, { recursive: true, force: true })));
|
|
});
|
|
|
|
describe('Works Square project package template', () => {
|
|
it('copies the shared baseline and only the Web example for a standard project', async () => {
|
|
const projectPath = await temporaryDirectory('niancode-works-template-web-');
|
|
const result = await initializeWorksPackageTemplate(projectPath, 'standard-development', { resourcesDirectory });
|
|
|
|
expect(result.files).toEqual(expect.arrayContaining([
|
|
'.dockerignore',
|
|
'Dockerfile',
|
|
'docker-compose.yml',
|
|
'nginx.conf',
|
|
'niancode.yml',
|
|
'deploy/works-deploy-check.schema.json',
|
|
'deploy/examples/works-deploy-check.web.json',
|
|
'deploy/部署报告模板.md',
|
|
]));
|
|
expect((await stat(join(projectPath, 'deploy/examples/works-deploy-check.web.json'))).isFile()).toBe(true);
|
|
await expect(stat(join(projectPath, 'deploy/examples/works-deploy-check.game.json'))).rejects.toMatchObject({ code: 'ENOENT' });
|
|
for (const releaseArtifact of ['works-deploy-check.json', 'works-publish.json', '部署报告.md']) {
|
|
await expect(stat(join(projectPath, releaseArtifact))).rejects.toMatchObject({ code: 'ENOENT' });
|
|
}
|
|
});
|
|
|
|
it('copies only the complete game/canvas example for a game project', async () => {
|
|
const projectPath = await temporaryDirectory('niancode-works-template-game-');
|
|
await initializeWorksPackageTemplate(projectPath, 'game-development', { resourcesDirectory });
|
|
|
|
const example = JSON.parse(await readFile(join(projectPath, 'deploy/examples/works-deploy-check.game.json'), 'utf8'));
|
|
const evidence = example.checks.game_canvas_smoke.canvas_evidence;
|
|
expect(example.status).toBe('BLOCKED');
|
|
expect(evidence.resize_verified).toBe(false);
|
|
for (const measurement of [
|
|
evidence.logical_resolution,
|
|
evidence.desktop_viewport,
|
|
evidence.mobile_viewport,
|
|
evidence.desktop_canvas,
|
|
evidence.mobile_canvas,
|
|
evidence.expected_max_canvas.desktop,
|
|
evidence.expected_max_canvas.mobile,
|
|
]) {
|
|
expect(typeof measurement.width).toBe('number');
|
|
expect(typeof measurement.height).toBe('number');
|
|
expect(measurement.width).toBeGreaterThan(0);
|
|
expect(measurement.height).toBeGreaterThan(0);
|
|
}
|
|
await expect(stat(join(projectPath, 'deploy/examples/works-deploy-check.web.json'))).rejects.toMatchObject({ code: 'ENOENT' });
|
|
});
|
|
|
|
it('keeps the manifest, Compose, Nginx, schema, and examples parseable and contract-safe', async () => {
|
|
const templateRoot = resolveWorksPackageTemplateDirectory(resourcesDirectory);
|
|
const manifest = parseYaml(await readFile(join(templateRoot, 'niancode.yml'), 'utf8'));
|
|
const compose = parseYaml(await readFile(join(templateRoot, 'docker-compose.yml'), 'utf8'));
|
|
const nginx = await readFile(join(templateRoot, 'nginx.conf'), 'utf8');
|
|
const schema = JSON.parse(await readFile(join(templateRoot, 'deploy/works-deploy-check.schema.json'), 'utf8'));
|
|
const web = JSON.parse(await readFile(join(templateRoot, 'deploy/examples/works-deploy-check.web.json'), 'utf8'));
|
|
const game = JSON.parse(await readFile(join(templateRoot, 'deploy/examples/works-deploy-check.game.json'), 'utf8'));
|
|
|
|
expect(manifest).toEqual({ runtime: 'compose', compose: { file: 'docker-compose.yml', public_service: 'frontend', public_port: 8080 } });
|
|
expect(compose.services.frontend.ports).toContain('127.0.0.1::8080');
|
|
expect(nginx).toContain('listen 0.0.0.0:8080');
|
|
expect(schema.properties.schema_version.const).toBe(1);
|
|
expect(web.checks).not.toHaveProperty('game_canvas_smoke');
|
|
expect(game.checks).toHaveProperty('game_canvas_smoke');
|
|
});
|
|
|
|
it('rejects invalid JSON with a source-relative error and never overwrites a destination file', async () => {
|
|
const fixtureResources = await temporaryDirectory('niancode-works-template-fixture-');
|
|
await cp(resolveWorksPackageTemplateDirectory(resourcesDirectory), resolveWorksPackageTemplateDirectory(fixtureResources), { recursive: true });
|
|
await writeFile(join(resolveWorksPackageTemplateDirectory(fixtureResources), 'deploy/examples/works-deploy-check.web.json'), '{invalid', 'utf8');
|
|
const invalidProject = await temporaryDirectory('niancode-works-template-invalid-');
|
|
await expect(initializeWorksPackageTemplate(invalidProject, 'standard-development', { resourcesDirectory: fixtureResources }))
|
|
.rejects.toThrow('deploy/examples/works-deploy-check.web.json');
|
|
|
|
const existingProject = await temporaryDirectory('niancode-works-template-existing-');
|
|
await writeFile(join(existingProject, 'niancode.yml'), 'user-owned', 'utf8');
|
|
await expect(initializeWorksPackageTemplate(existingProject, 'standard-development', { resourcesDirectory }))
|
|
.rejects.toThrow('niancode.yml');
|
|
expect(await readFile(join(existingProject, 'niancode.yml'), 'utf8')).toBe('user-owned');
|
|
});
|
|
|
|
it('is included by the packaged resources rule', async () => {
|
|
const builderConfig = parseYaml(await readFile(join(process.cwd(), 'electron-builder.yml'), 'utf8'));
|
|
expect(builderConfig.extraResources).toEqual(expect.arrayContaining([
|
|
expect.objectContaining({ from: 'resources/', to: 'resources/' }),
|
|
]));
|
|
expect((await stat(resolveWorksPackageTemplateDirectory(resourcesDirectory))).isDirectory()).toBe(true);
|
|
});
|
|
});
|