validate: removed-from-sale IAPs are no blockers; shots capture --device landscape (Mac / Chromebook frames from a web build); playwright lookup says why each place failed
This commit is contained in:
parent
cdd9321794
commit
d0f568ebc2
13 changed files with 230 additions and 48 deletions
|
|
@ -202,7 +202,9 @@ export interface ShotsConfig {
|
|||
/** Tablet frames: default 1032 x 1352 @2x (13" iPad minus its 24 px bar) with apple.ipad, else 800 x 1240 @2x. */
|
||||
tablet?: { w: number; h: number; dpr?: number; bar?: number };
|
||||
/** Landscape layout (Mac, Chromebook, and the Play tablet slots with playTablets): copy on one side, the frame on the
|
||||
* other. Frames: <frames>/<lang>/landscape/<name>.png at `size` CSS px (default 1440 x 900 @2x). frame: 'window' (a
|
||||
* other. Frames: <frames>/<lang>/landscape/<name>.png at `size` CSS px (default 1440 x 900 @2x = 2880 x 1800), made by
|
||||
* `shots capture --device landscape` (web source: a desktop viewport, no touch, no status bar crop; ios-sim / android-emu
|
||||
* have no landscape device, use a web build or a folder). frame: 'window' (a
|
||||
* Mac window around the game, the default for mac) or 'none' (the bare frame, the default for Play sizes). side:
|
||||
* where the copy goes (default 'left'). playTablets: render tab7 / tab10 landscape (1920 x 1080 / 2560 x 1440). */
|
||||
landscape?: { frame?: 'window' | 'none'; side?: 'left' | 'right'; playTablets?: boolean; size?: { w: number; h: number; dpr?: number } };
|
||||
|
|
@ -217,7 +219,7 @@ export interface ShotsConfig {
|
|||
/** One raw frame. Web: `path` (+ query). Simulator / emulator: `url` deep link and / or `args` / `extras`. */
|
||||
/** Web capture: a selector, candidates tried in order, or per layout { phone, tablet, any } (own list first). Prefer data
|
||||
* attributes (`[data-shot="shop-row"]`); `text=Shop` and `role=button[name="Shop"]` work as fallbacks. */
|
||||
export type ShotSelector = string | string[] | { phone?: string | string[]; tablet?: string | string[]; any?: string | string[] };
|
||||
export type ShotSelector = string | string[] | { phone?: string | string[]; tablet?: string | string[]; landscape?: string | string[]; any?: string | string[] };
|
||||
|
||||
export interface ShotSpec {
|
||||
/** Web: wait (up to the waitFor timeout) until one of these matches, before waitFor; a miss names the selector and the size. */
|
||||
|
|
@ -235,7 +237,10 @@ export interface ShotSpec {
|
|||
delayMs?: number;
|
||||
/** Web: JS run in the page before the screenshot. */
|
||||
eval?: string[];
|
||||
device?: 'phone' | 'tablet' | 'both';
|
||||
/** Which frames this shot makes: phone (default), tablet, both (phone + tablet), landscape (<lang>/landscape/<name>.png at
|
||||
* shots.landscape.size, web source only). When the project renders a landscape size (apple.mac, play.chromebook,
|
||||
* shots.landscape.playTablets), shots without a device and 'both' shots also make a landscape frame. */
|
||||
device?: 'phone' | 'tablet' | 'both' | 'landscape';
|
||||
/** Web, capture.session "per-language": how this shot is reached in the already booted page. "spa" (default):
|
||||
* history.pushState to its path + a popstate event, then waitFor / eval (switch scenes from those); "reload":
|
||||
* page.goto in the same page. A failing waitFor there falls back to a fresh page. */
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ export interface PlaySnapshot {
|
|||
const EDITABLE = ['PREPARE_FOR_SUBMISSION', 'DEVELOPER_REJECTED', 'REJECTED', 'METADATA_REJECTED', 'INVALID_BINARY'];
|
||||
const IN_FLIGHT = ['WAITING_FOR_REVIEW', 'IN_REVIEW', 'PENDING_DEVELOPER_RELEASE', 'PROCESSING_FOR_DISTRIBUTION', 'READY_FOR_DISTRIBUTION', 'READY_FOR_SALE', 'ACCEPTED'];
|
||||
const IAP_OK = ['READY_TO_SUBMIT', 'WAITING_FOR_REVIEW', 'IN_REVIEW', 'APPROVED'];
|
||||
const IAP_OFF_SALE = ['DEVELOPER_REMOVED_FROM_SALE', 'REMOVED_FROM_SALE'];
|
||||
/** Age rating questions whose null means "not answered" (kidsAgeBand null = not a kids app). */
|
||||
// Left null on apps App Review accepted: gracRatingClassificationNumber (a Korean GRAC rating number, for apps GRAC
|
||||
// rated), socialMediaAgeRestricted (asked only when socialMedia is true). socialMedia itself is a newer question that
|
||||
|
|
@ -281,12 +282,17 @@ export function appleRules(s: AppleSnapshot): Finding[] {
|
|||
|
||||
const iaps = new Map(s.iaps.map(i => [i.productId, i] as const));
|
||||
for (const id of s.expectedProducts) if (!iaps.has(id)) add('blocker', 'iap', `${id}: not on App Store Connect`, 'storebound apple iap');
|
||||
const expected = new Set(s.expectedProducts);
|
||||
for (const i of s.iaps) {
|
||||
// a product the developer took off sale (old consumables, a retired unlock) does not block a submission; it matters
|
||||
// only when the config still sells it
|
||||
if (IAP_OFF_SALE.includes(i.state)) { if (expected.has(i.productId)) add('warn', 'iap', `${i.productId}: ${i.state}, but the config still sells it`, 'put it back on sale in App Store Connect, or drop it from products'); continue; }
|
||||
if (!IAP_OK.includes(i.state)) add('blocker', 'iap', `${i.productId}: ${i.state}`, i.state === 'MISSING_METADATA' ? 'storebound apple iap + apple iap-review' : 'see the review-rejections skill');
|
||||
else if (!i.reviewScreenshot && i.state !== 'APPROVED') add('blocker', 'iap', `${i.productId}: no review screenshot`, 'storebound apple iap-review');
|
||||
}
|
||||
for (const g of s.firstProductGaps ?? []) add(g.level === 'error' ? 'blocker' : 'warn', 'iap-first', g.msg, g.fix);
|
||||
if (s.iaps.length && !f.some(x => x.code === 'iap')) add('ok', 'iap', `${s.iaps.length} in-app purchases ready`);
|
||||
const onSale = s.iaps.filter(i => !IAP_OFF_SALE.includes(i.state)).length, offSale = s.iaps.length - onSale;
|
||||
if (s.iaps.length && !f.some(x => x.code === 'iap')) add('ok', 'iap', `${onSale} in-app purchases ready${offSale ? ` (${offSale} removed from sale, not checked)` : ''}`);
|
||||
|
||||
f.push(...appleSubscriptionRules(s));
|
||||
for (const m of s.capabilityMismatches ?? []) add('warn', 'capabilities', m.msg, m.fix);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue