From bf2208e77b6de84c10bb9458ff9c3e9815c297f0 Mon Sep 17 00:00:00 2001 From: Jeppe B <2jepp9350@gmail.com> Date: Thu, 23 Jul 2026 16:50:37 +0200 Subject: [PATCH] fix(ios): use valid Danish TestFlight locale (#222) Use Apple's supported `da` beta locale and cover the localization/distribution flow with a regression test. The first signed upload already processed version 1.0.0 build 1 successfully; this fixes the post-processing localization failure before the controlled retry. --- scripts/mobile/app-store-connect.mjs | 5 ++-- tests/node/app-store-connect.test.mjs | 40 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/scripts/mobile/app-store-connect.mjs b/scripts/mobile/app-store-connect.mjs index e1b3027d..7c3997f0 100644 --- a/scripts/mobile/app-store-connect.mjs +++ b/scripts/mobile/app-store-connect.mjs @@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url"; export const APP_STORE_CONNECT_BASE_URL = "https://api.appstoreconnect.apple.com/v1"; export const APP_STORE_CONNECT_V2_BASE_URL = "https://api.appstoreconnect.apple.com/v2"; export const EXPECTED_RELEASE_TYPE = "AFTER_APPROVAL"; +export const TESTFLIGHT_BETA_LOCALE = "da"; export const EXPECTED_AVAILABLE_TERRITORIES = ["DNK"]; const defaultSleep = (milliseconds) => new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds)); @@ -198,7 +199,7 @@ export const createAppStoreConnectClient = ({ const groupId = required("TESTFLIGHT_INTERNAL_GROUP_ID"); const localizationParams = new URLSearchParams({ "filter[build]": build.id, - "filter[locale]": "da-DK", + "filter[locale]": TESTFLIGHT_BETA_LOCALE, }); const localizations = await request(`/betaBuildLocalizations?${localizationParams}`); const existingLocalization = (localizations?.data ?? [])[0]; @@ -220,7 +221,7 @@ export const createAppStoreConnectClient = ({ body: JSON.stringify({ data: { type: "betaBuildLocalizations", - attributes: { locale: "da-DK", whatsNew }, + attributes: { locale: TESTFLIGHT_BETA_LOCALE, whatsNew }, relationships: { build: { data: { type: "builds", id: build.id } } }, }, }), diff --git a/tests/node/app-store-connect.test.mjs b/tests/node/app-store-connect.test.mjs index eb2d556a..9d412f4f 100644 --- a/tests/node/app-store-connect.test.mjs +++ b/tests/node/app-store-connect.test.mjs @@ -5,6 +5,7 @@ import { APP_STORE_CONNECT_BASE_URL, APP_STORE_CONNECT_V2_BASE_URL, EXPECTED_RELEASE_TYPE, + TESTFLIGHT_BETA_LOCALE, appStoreVersionsPath, createAppStoreConnectClient, } from "../../scripts/mobile/app-store-connect.mjs"; @@ -280,6 +281,45 @@ test("rejects broader availability, preorder, or automatic future territories", ); }); +test("uses Apple's Danish beta locale when localizing and distributing a processed build", async () => { + const environment = baseEnvironment({ + TESTFLIGHT_INTERNAL_GROUP_ID: "internal-qa", + TESTFLIGHT_WHAT_TO_TEST: "Test den verificerede build.", + }); + const { client, calls, outputs } = makeClient({ + environment, + handler: (url, options) => { + if (url.includes("/builds?")) { + return jsonResponse({ + data: [{ type: "builds", id: "build-8", attributes: { version: "8", processingState: "VALID" } }], + }); + } + if (url.includes("/betaBuildLocalizations?")) return jsonResponse({ data: [] }); + if (url.endsWith("/betaBuildLocalizations") && options.method === "POST") { + return jsonResponse({ data: { type: "betaBuildLocalizations", id: "localization-1" } }, 201); + } + if (url.includes("/betaGroups/internal-qa/relationships/builds?")) return jsonResponse({ data: [] }); + if (url.endsWith("/betaGroups/internal-qa/relationships/builds") && options.method === "POST") { + return jsonResponse(null, 204); + } + throw new Error(`Unexpected URL ${url}`); + }, + }); + + assert.equal(TESTFLIGHT_BETA_LOCALE, "da"); + assert.equal((await client.waitAndDistribute()).id, "build-8"); + const localizationLookup = calls.find(({ url }) => url.includes("/betaBuildLocalizations?")); + assert.match(localizationLookup.url, /filter%5Blocale%5D=da(?:&|$)/u); + const localizationCreate = calls.find( + ({ url, options }) => url.endsWith("/betaBuildLocalizations") && options.method === "POST" + ); + assert.deepEqual(JSON.parse(localizationCreate.options.body).data.attributes, { + locale: "da", + whatsNew: "Test den verificerede build.", + }); + assert.deepEqual(outputs, [["app_store_build_id", "build-8"]]); +}); + test("retries transient responses without exposing the bearer token in errors", async () => { const { client, calls, sleeps } = makeClient({ handler: (_url, _options, attempt) => {