storebound/tests/console-links.test.ts

83 lines
4.8 KiB
TypeScript

// Console deep links (pitfall 71): every page key builds an absolute https URL with and without ids, and no message in
// cli/ sends the owner to "App Store Connect > ..." / "Play Console > ..." / "App content > ..." without a URL.
import { afterEach, describe, expect, test } from 'bun:test';
import { readdirSync, readFileSync, statSync } from 'node:fs';
import { join } from 'node:path';
import { CONSOLE_PAGES, type ConsolePage, consoleLink, consoleUrl, consoleUrlExact, playTrackPage, setConsoleIds } from '../cli/lib/console-links';
const IDS = { appleAppId: '1000000001', playDeveloperId: '5550000000000000001', playAppId: '4970000000000000002' };
const PAGES = Object.keys(CONSOLE_PAGES) as ConsolePage[];
afterEach(() => setConsoleIds({}));
describe('console-links', () => {
test('every page: absolute https URL without ids', () => {
for (const p of PAGES) {
const u = consoleUrl(p, {});
expect(u).toMatch(/^https:\/\/(appstoreconnect\.apple\.com|play\.google\.com\/console)/);
expect(() => new URL(u)).not.toThrow();
expect(consoleLink(p, '', {})).toContain(`: ${u}`);
}
});
test('every page: id-specific URL with ids', () => {
for (const p of PAGES) {
const u = consoleUrl(p, IDS);
expect(u.startsWith('https://')).toBe(true);
expect(consoleUrlExact(p, IDS)).toBe(true);
const page = CONSOLE_PAGES[p];
if (page.scope === 'app') expect(u).toContain(page.store === 'apple' ? `/apps/${IDS.appleAppId}/` : `/developers/${IDS.playDeveloperId}/app/${IDS.playAppId}/`);
if (page.store === 'play' && page.scope === 'developer') expect(u).toContain(`/developers/${IDS.playDeveloperId}/`);
}
});
test('fallbacks: app list / developer chooser; reused verified shapes', () => {
expect(consoleUrl('asc.age-rating', {})).toBe('https://appstoreconnect.apple.com/apps');
expect(consoleUrl('play.data-safety', {})).toBe('https://play.google.com/console');
expect(consoleUrl('play.data-safety', { playDeveloperId: IDS.playDeveloperId })).toBe(`https://play.google.com/console/u/0/developers/${IDS.playDeveloperId}/app-list`);
expect(consoleUrl('play.dashboard', IDS)).toBe(`https://play.google.com/console/u/0/developers/${IDS.playDeveloperId}/app/${IDS.playAppId}/app-dashboard`);
expect(consoleUrl('play.content-rating', IDS)).toEndWith('/app-content/content-rating-overview');
expect(consoleUrl('play.create-app', IDS)).toEndWith(`/developers/${IDS.playDeveloperId}/create-new-app`);
expect(consoleUrl('asc.keys', {})).toBe('https://appstoreconnect.apple.com/access/integrations/api');
expect(consoleUrlExact('asc.app-privacy', {})).toBe(false);
expect(consoleUrl('asc.app-info', { appleAppId: 'not-a-number' })).toBe('https://appstoreconnect.apple.com/apps');
});
test('ids remembered from the loaded config', () => {
setConsoleIds(IDS);
expect(consoleUrl('asc.app-privacy')).toBe(`https://appstoreconnect.apple.com/apps/${IDS.appleAppId}/distribution/privacy`);
expect(consoleLink('play.app-access')).toContain(`/app/${IDS.playAppId}/app-content/testing-credentials`);
expect(playTrackPage('production')).toBe('play.production');
expect(playTrackPage('beta')).toBe('play.releases');
});
});
describe('guard: no plain console directions in cli/', () => {
const PLAIN = /App Store Connect >|Play Console >|App content >/;
const LINKED = /https:\/\/|consoleLink\(|consoleUrl\(|\.url\b|\$\{\w*[Uu]rl\}/; // ${url}: a variable that holds the absolute link
const SKIP = new Set(['console-links.ts', 'embedded.ts']); // embedded.ts is generated from templates/ (checked below)
const files = (dir: string): string[] => readdirSync(dir).flatMap(n => {
const p = join(dir, n);
return statSync(p).isDirectory() ? files(p) : p.endsWith('.ts') && !p.endsWith('.test.ts') && !SKIP.has(n) ? [p] : [];
});
test('every message line with a console path carries an https URL or goes through console-links', () => {
const bad: string[] = [];
for (const f of files('cli')) {
readFileSync(f, 'utf8').split('\n').forEach((line, i) => {
const t = line.trim();
if (t.startsWith('//') || t.startsWith('*') || t.startsWith('/*')) return; // comments are not messages
if (PLAIN.test(line) && !LINKED.test(line)) bad.push(`${f}:${i + 1}: ${t.slice(0, 120)}`);
});
}
expect(bad).toEqual([]);
});
test('templates: console directions in the generated docs carry a URL', () => {
const bad: string[] = [];
const walk = (dir: string): string[] => readdirSync(dir).flatMap(n => { const p = join(dir, n); return statSync(p).isDirectory() ? walk(p) : p.endsWith('.md') ? [p] : []; });
for (const f of walk('templates'))
readFileSync(f, 'utf8').split('\n').forEach((line, i) => { if (/App Store Connect >|Play Console >/.test(line) && !/https:\/\//.test(line)) bad.push(`${f}:${i + 1}`); });
expect(bad).toEqual([]);
});
});