store-only apps edit their listing on the store (storebound listing live / set: old -> new per field with the checks, a PATCH of only the edited fields; serve listing.set is the one store-only write, its token bound to the store ids; policy.rs STORE_ONLY_WRITES, write_with_confirm for store targets); screenshot captions per language and scene edited as a text edit of the shots copy JSON (JSON files keep their layout); Play content rating, target audience and App content answers in App info; docs/console-parity.md after the editors (73 rows: 55 complete, 18 with the reason) and tests/console-parity.test.ts keeping it honest

This commit is contained in:
CRTkafa 2026-10-14 00:53:38 +08:00
parent 3cea2af4e1
commit 7ac028421a
33 changed files with 852 additions and 157 deletions

View file

@ -80,6 +80,8 @@ describe('config-path: one value set by path, the rest of the file byte for byte
expect(out).toContain("apple: { bundleId: 'com.example.app', info: { reviewNotes: 'Line one\\nIt\\'s line two' } },");
const app = setByPath(CFG, 'storebound.config.ts', 'app.marketingUrl', 'https://example.com/m');
expect(app).toContain(" contactEmail: 'support@example.com',\n marketingUrl: 'https://example.com/m',\n },");
// after a last member without a trailing comma (and its comment)
expect(setByPath('export default {\n a: 1 // one\n};\n', 'c.ts', 'b', 2)).toBe('export default {\n a: 1, // one\n b: 2,\n};\n');
});
test('removes a key; the edited file still loads to the same object otherwise', async () => {
const out = setByPath(CFG, 'storebound.config.ts', 'app.website', undefined);
@ -301,3 +303,30 @@ describe('CLI: storebound edit on a copy of the demo app', () => {
expect(statSync(f).mode & 0o777).toBe(0o600);
});
});
describe('JSON files edited as text (the shots copy file keeps one scene per line)', () => {
test('a caption line changes, the other lines and the layout stay', async () => {
const dir = mkdtempSync(join(tmpdir(), 'sb-copy-'));
cpSync(join(ROOT, 'examples', 'demo-app'), dir, { recursive: true });
const file = join(dir, 'store', 'shots', 'copy.json');
const before = readFileSync(file, 'utf8');
const ops = JSON.stringify([{ kind: 'shotsCopy', lang: 'en', scene: 'lists', field: 'title', line: 1, value: 'in one tidy place' }, { kind: 'shotsCopy', lang: 'tr', scene: 'share', field: 'sub', value: 'Birlikte düzenleyin.' }]);
const r = await Bun.$`bun ${CLI} edit --ops ${ops}`.cwd(dir).quiet().nothrow();
expect(r.exitCode).toBe(0);
const after = readFileSync(file, 'utf8');
const changed = after.split('\n').filter((l, i) => l !== before.split('\n')[i]);
expect(changed).toHaveLength(2);
expect(changed[0]).toContain('"title": ["Every list,","in one tidy place"]');
expect(JSON.parse(after).tr.share.sub).toBe('Birlikte düzenleyin.');
expect(after.split('\n').length).toBe(before.split('\n').length);
});
test('setByPath on JSON: insert and remove without trailing commas', () => {
const j = '{\n "a": { "x": 1 },\n "b": 2\n}\n';
const ins = setByPath(j, 'f.json', 'c', 'new');
expect(JSON.parse(ins)).toEqual({ a: { x: 1 }, b: 2, c: 'new' });
expect(ins).toContain(' "b": 2,\n "c": "new"\n}');
const rem = setByPath(j, 'f.json', 'b', undefined);
expect(JSON.parse(rem)).toEqual({ a: { x: 1 } });
expect(JSON.parse(setByPath(j, 'f.json', 'a.y', [1, 2]))).toEqual({ a: { x: 1, y: [1, 2] }, b: 2 });
});
});