apple submit --build <n> (first submission, the resubmit steps; plan table with --json under serve); desktop owner actions to send for review: owner.apple_submit, owner.apple_resubmit, owner.apple_iap_submit, owner.play_production, and the price gate owner actions (owner.prices_play_products / _play_subscriptions / _apple_subscriptions; owner.prices_import passes --allow-price-changes after the dialog); OWNER_ALLOWED_ARGS ties each forbidden flag to its one action, dry runs never carry owner flags, MCP still has no submit or production release; submit dialog text in the host; system.tools androidSdk, project.inspect playRelease, declarations dataDeletion
This commit is contained in:
parent
2e7dd44ae6
commit
33eb24a3dc
17 changed files with 335 additions and 49 deletions
|
|
@ -13,6 +13,7 @@ import { notReadyYet, otherSubmissionOf, submitTiming } from '../cli/apple/submi
|
|||
import { run as appleIapSubmit } from '../cli/apple/iap-submit';
|
||||
import { run as appleResubmit } from '../cli/apple/resubmit';
|
||||
import { run as appleStatus } from '../cli/apple/status';
|
||||
import { run as appleSubmit } from '../cli/apple/submit';
|
||||
import { ascEnv, BASE, capture, project, restore } from './helpers/store-mock';
|
||||
import { installStores, type Stores, uninstallStores, writes } from './helpers/fake-stores';
|
||||
|
||||
|
|
@ -177,6 +178,62 @@ describe('apple resubmit --build <n>', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('apple submit --build <n> (a first submission, the desktop owner action)', () => {
|
||||
/** Version 1.0.0 in Prepare for Submission without a build, build 8 VALID, the gems IAP ready, no open submission. */
|
||||
function fresh(): void {
|
||||
s.asc.put({ type: 'appStoreVersions', id: 'ver-1', attributes: { versionString: '1.0.0', appVersionState: 'PREPARE_FOR_SUBMISSION', platform: 'IOS' }, rels: { app: APP } });
|
||||
s.asc.put({ type: 'builds', id: 'b-8', attributes: { version: '8', processingState: 'VALID', usesNonExemptEncryption: false }, rels: { app: APP } });
|
||||
s.asc.put({ type: 'inAppPurchases', id: 'iap-gems', attributes: { productId: 'com.example.notes.gems', inAppPurchaseType: 'CONSUMABLE', state: 'READY_TO_SUBMIT' }, rels: { app: APP } });
|
||||
s.asc.put({ type: 'inAppPurchaseVersions', id: 'iapv-gems', attributes: { version: 1, state: 'PREPARE_FOR_SUBMISSION' }, rels: { inAppPurchase: 'iap-gems' } });
|
||||
}
|
||||
const ONE: ShipConfig = { ...CONFIG, products: CONFIG.products!.slice(0, 1) };
|
||||
|
||||
test('dry run: plan table (build attach, version + IAP), nothing sent; under serve the table comes with the --json result', async () => {
|
||||
fresh();
|
||||
const p = project(ONE);
|
||||
const a = s.calls.length;
|
||||
const r = await capture(() => appleSubmit(p.ctx(['--build', '8'])));
|
||||
expect(r.exit).toBeFalsy();
|
||||
expect(shown(a)).toEqual([]);
|
||||
expect(r.out).toMatch(/build\s+build 8\s+none attached\s+attach build 8/);
|
||||
expect(r.out).toMatch(/submission\s+new draft\s+-\s+create/);
|
||||
expect(r.out).toMatch(/app version 1\.0\.0/);
|
||||
expect(r.out).toContain('com.example.notes.gems');
|
||||
expect(r.out).toContain('Submitting is the owner');
|
||||
process.env.STOREBOUND_MACHINE = '1';
|
||||
try {
|
||||
const j = await capture(() => appleSubmit(p.ctx(['--build', '8', '--json'])));
|
||||
expect(j.out).toContain('STEP');
|
||||
const line = j.out.split('\n').find(l => l.includes('storebound-result'))!;
|
||||
expect(JSON.parse(line.slice(line.indexOf('{')))).toMatchObject({ dryRun: true, version: { versionString: '1.0.0', state: 'PREPARE_FOR_SUBMISSION' }, build: { action: 'attach', id: 'b-8' }, blockers: [] });
|
||||
} finally { delete process.env.STOREBOUND_MACHINE; }
|
||||
expect(shown(a)).toEqual([]);
|
||||
});
|
||||
|
||||
test('--yes: build attached, one draft with the version and the IAP, submitted; a re-run sends nothing', async () => {
|
||||
fresh();
|
||||
const p = project(ONE);
|
||||
const a = s.calls.length;
|
||||
const r = await capture(() => appleSubmit(p.ctx(['--build', '8', '--yes'])));
|
||||
expect([r.exit ?? 0, r.err]).toEqual([0, '']);
|
||||
const sid = s.asc.all('reviewSubmissions')[0]!.id;
|
||||
expect(shown(a)).toEqual(['PATCH /v1/appStoreVersions/ver-1/relationships/build', 'POST /v1/reviewSubmissions', 'POST /v1/reviewSubmissionItems', 'POST /v1/reviewSubmissionItems', `PATCH /v1/reviewSubmissions/${sid}`]);
|
||||
expect(s.asc.all('reviewSubmissions').map(x => x.attributes.state)).toEqual(['WAITING_FOR_REVIEW']);
|
||||
const b = s.calls.length;
|
||||
const again = await capture(() => appleSubmit(p.ctx(['--build', '8', '--yes'])));
|
||||
expect(again.exit).toBeFalsy();
|
||||
expect(shown(b)).toEqual([]);
|
||||
});
|
||||
|
||||
test('a live version is not submitted; no editable version is a clear stop', async () => {
|
||||
fresh();
|
||||
s.asc.res.get('ver-1')!.attributes.appVersionState = 'READY_FOR_SALE';
|
||||
const p = project(ONE);
|
||||
await expect(appleSubmit(p.ctx(['--build', '8', '--version', '1.0.0']))).rejects.toThrow(/READY_FOR_SALE: only a version in Prepare for Submission/);
|
||||
await expect(appleSubmit(p.ctx(['--build', '8']))).rejects.toThrow(/no App Store version to submit/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('apple iap-submit with a submission back from App Review', () => {
|
||||
test('--with-version --yes reuses UNRESOLVED_ISSUES: no new draft, the missing item only, then resubmitted; a re-run writes nothing', async () => {
|
||||
rejected({ ver: 'READY_FOR_REVIEW' }); // the version item was resolved already
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue