From 327a85e042bae20e438037f390a5450736093cd6 Mon Sep 17 00:00:00 2001 From: CRT Date: Wed, 21 Oct 2026 05:39:45 +0700 Subject: [PATCH] console verify: an older page's long labelled radio still counts from page text when the sheet answers Yes / No; tests and the teaser follow the new sign-in details question and the added App Privacy step --- cli/lib/console-verify.ts | 3 +++ media/teaser/script.js | 6 +++--- tests/guest-login-wall.test.ts | 8 ++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cli/lib/console-verify.ts b/cli/lib/console-verify.ts index b23359a..797e227 100644 --- a/cli/lib/console-verify.ts +++ b/cli/lib/console-verify.ts @@ -388,6 +388,9 @@ export function verifyPlayDeclaration(form: Exclude !/^(yes|no)$/i.test(l) && blob.includes(fold(l))) : undefined; + if (older) { rows.push({ item: a.question, expected: want, seen: older, status: 'match' }); continue; } const plain = !want || /^(yes|no)$/i.test(want) || a.action === 'untick'; if (plain) { rows.push({ item: a.question, expected: a.text, seen: 'not readable from page text', status: 'not-found', note: 'pass the input states: run the plan\'s verify.capture snippet and `--from -`' }); continue; } const shownAs = a.action === 'type' || a.action === 'choose' ? (blob.includes(fold(want)) ? want : null) : labels.find(l => blob.includes(fold(l))) ?? null; diff --git a/media/teaser/script.js b/media/teaser/script.js index a3c5166..844967c 100644 --- a/media/teaser/script.js +++ b/media/teaser/script.js @@ -136,9 +136,9 @@ export const PRIVACY_ANSWERS = [ export const PRIVACY_STEPS = [ ' 1. Open App Store Connect > Apps > the app > App Privacy (sidebar): https://appstoreconnect.apple.com/apps', ' 2. First time: click "Get Started" and answer the Data Collection question; then Next. [click: "Get Started"]', - ' 5. "Email Address" step 1, purposes: tick and untick the 6 checkboxes as answered, then Next. [click: "Next"]', - ' 6. "Email Address" step 2, linked to the user: pick the radio, then Next. [click: "Next"]', - ' 8. "Email Address" step 5, tracking: pick the radio, then Save (a draft). Never "Definitions and Examples". The dialog closes; the row shows Edit and the summary. [click: "Save"]', + ' 6. "Email Address" step 1, purposes: tick and untick the 6 checkboxes as answered, then Next. [click: "Next"]', + ' 7. "Email Address" step 2, linked to the user: pick the radio, then Next. [click: "Next"]', + ' 9. "Email Address" step 5, tracking: pick the radio, then Save (a draft). Never "Definitions and Examples". The dialog closes; the row shows Edit and the summary. [click: "Save"]', ]; export const DATA_SAFETY_CSV = [ 'PSL_DATA_COLLECTION_COLLECTS_PERSONAL_DATA,,TRUE,REQUIRED,Does your app collect or share any of the required user data types?', diff --git a/tests/guest-login-wall.test.ts b/tests/guest-login-wall.test.ts index 8390298..b62f699 100644 --- a/tests/guest-login-wall.test.ts +++ b/tests/guest-login-wall.test.ts @@ -57,7 +57,7 @@ describe('check: guest-play apps that Play may read as a login wall', () => { describe('plan play-sign-in-details: guest steps', () => { test('instructions with no sign-in: restricted, one entry without credentials, the box is the owner\'s', () => { const p = consolePlan('play-sign-in-details', guest); - expect(p.answers.find(a => a.id === 'access')).toMatchObject({ value: true, status: 'certain', label: 'All or some functionality in my app is restricted' }); + expect(p.answers.find(a => a.id === 'access')).toMatchObject({ value: true, status: 'certain', label: 'Yes' }); expect(p.answers.find(a => a.id === 'entryName')).toMatchObject({ value: GUEST_ENTRY_NAME, status: 'certain', action: 'type' }); expect(p.answers.find(a => a.id === 'credentials')).toMatchObject({ value: null, status: 'certain', text: 'leave both empty (no account)' }); expect(p.answers.find(a => a.id === 'credentials')!.action).toBeUndefined(); @@ -65,7 +65,7 @@ describe('plan play-sign-in-details: guest steps', () => { expect(p.answers.find(a => a.id === 'fullAccess')).toMatchObject({ status: 'agreement', value: null, control: 'checkbox', label: PLAY_FULL_ACCESS_BOX }); expect(p.answers.find(a => a.id === 'fullAccess')!.action).toBeUndefined(); // the agent presses Add instructions, Add and Save; the box and Send changes for review are the owner's - expect(p.buttons.allowed.map(b => b.button)).toEqual(expect.arrayContaining(['Add instructions', 'Add', 'Save'])); + expect(p.buttons.allowed.map(b => b.button)).toEqual(expect.arrayContaining(['Add details', 'Add', 'Save'])); expect(p.buttons.final).toBe('Send changes for review'); expect(p.buttons.neverPress).toContain('Send changes for review'); expect(p.ownerBatch.map(o => [o.when, o.kind])).toEqual([['after', 'agreement'], ['after', 'final']]); @@ -80,7 +80,7 @@ describe('plan play-sign-in-details: guest steps', () => { test('verify reads the entry name and the steps back, never the box', () => { const page = `Sign in details\nAll or some functionality in my app is restricted\n${GUEST_ENTRY_NAME}\n${STEPS}`; const r = verifyPlayDeclaration('play-sign-in-details', guest, page); - expect(r.rows.filter(x => x.status === 'match').map(x => x.item)).toEqual(['Is any functionality restricted (sign-in, membership, location)?', 'Name of the instructions entry', 'Instructions for the reviewer']); + expect(r.rows.filter(x => x.status === 'match').map(x => x.item)).toEqual(['Is any part of your app restricted?', 'Name of the instructions entry', 'Instructions for the reviewer']); expect(r.rows.some(x => /full access/.test(x.item))).toBe(false); expect(r.mismatches).toBe(0); }); @@ -93,7 +93,7 @@ describe('plan play-sign-in-details: guest steps', () => { test('no instructions: the "No restrictions" path stays', () => { const p = consolePlan('play-sign-in-details', { ...base, reviewAccess: { signInRequired: false } } as ShipConfig); expect(p.answers.map(a => [a.id, a.value])).toEqual([['access', false]]); - expect(p.answers[0]!.label).toBe('All functionality in my app is available without any access restrictions'); + expect(p.answers[0]!.label).toBe('No'); expect(p.buttons.allowed.map(b => b.button)).not.toContain('Add'); expect(p.ownerBatch.map(o => o.kind)).toEqual(['final']); });