@aerokit/sdk 14.68.0 → 14.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aerokit/sdk",
3
- "version": "14.68.0",
3
+ "version": "14.70.0",
4
4
  "description": "Unified TypeScript SDK for modern cloud platforms.",
5
5
  "scripts": {
6
6
  "clean": "rm -rf dist",
@@ -1,6 +1,11 @@
1
1
  import { expect, test } from '../fixtures.js';
2
2
  import { listCrumb } from './list.js';
3
3
 
4
+ const LANGUAGE_KEY = 'codbex.harmonia.language';
5
+ // A reload in another language is three round trips on a cold instance - the page, the list's
6
+ // data GET and the translation overlay behind it - so the 5 s default flaked on slower runners.
7
+ const OVERLAY_TIMEOUT = 15_000;
8
+
4
9
  // The read-time translation overlay: switch the shared language key (what the Region &
5
10
  // Language setting writes), reload, and a known seed row shows its translated name.
6
11
  // Needs a concrete sample in the manifest: { language, base, translated }.
@@ -11,11 +16,53 @@ export function multilingualFlow(manifest, entity) {
11
16
  test(`${entity.name}: ${sample.language} translation overlays on read`, async ({ page }) => {
12
17
  await page.goto(manifest.standaloneShell + entity.route);
13
18
  await expect(listCrumb(page, entity)).toBeVisible();
14
- await page.evaluate((lang) => localStorage.setItem('codbex.harmonia.language', lang), sample.language);
15
- await page.reload();
16
- await expect(page.locator('tbody tr', { hasText: sample.translated }).first()).toBeVisible();
17
- await page.evaluate(() => localStorage.setItem('codbex.harmonia.language', 'en'));
18
- await page.reload();
19
- await expect(page.locator('tbody tr', { hasText: sample.base }).first()).toBeVisible();
19
+ const translatedRead = await reloadIn(page, manifest, entity, sample.language);
20
+ await expectRowShowing(page, sample.translated, translatedRead);
21
+ const baseRead = await reloadIn(page, manifest, entity, 'en');
22
+ await expectRowShowing(page, sample.base, baseRead);
20
23
  });
21
24
  }
25
+
26
+ // Set the language key and reload, waiting for the list's own data GET (the paged getAll on the
27
+ // entity's controller) so the assertion starts once the rows are on their way, and answering what
28
+ // that read asked for and got back - which is what tells a slow render from a missing translation.
29
+ // The wait is registered only once the reload has committed: registered before it, it also matched
30
+ // a list GET still in flight from the document the reload replaced, whose body is gone (#7584). The
31
+ // new document issues its own GET only after its scripts have loaded, so it cannot slip past.
32
+ async function reloadIn(page, manifest, entity, language) {
33
+ await page.evaluate(([key, lang]) => localStorage.setItem(key, lang), [LANGUAGE_KEY, language]);
34
+ const controllerPath = manifest.restBase + entity.api;
35
+ await page.reload({ waitUntil: 'commit' });
36
+ const response = await page.waitForResponse(
37
+ (candidate) => candidate.request().method() === 'GET' && new URL(candidate.url()).pathname === controllerPath,
38
+ { timeout: OVERLAY_TIMEOUT },
39
+ );
40
+ return {
41
+ language,
42
+ acceptLanguage: await response.request().headerValue('accept-language'),
43
+ status: response.status(),
44
+ // Only the diagnostic reads the body, so a body the browser no longer holds must not fail the flow.
45
+ body: await response.text().catch(() => null),
46
+ };
47
+ }
48
+
49
+ async function expectRowShowing(page, text, read) {
50
+ const rows = page.locator('tbody tr');
51
+ try {
52
+ await expect(rows.filter({ hasText: text }).first()).toBeVisible({ timeout: OVERLAY_TIMEOUT });
53
+ } catch (error) {
54
+ const firstRow = (await rows.allInnerTexts())[0]?.replace(/\s+/g, ' ').trim();
55
+ throw new Error(
56
+ `no list row shows '${text}' after reloading in '${read.language}': the list GET sent Accept-Language ` +
57
+ `${read.acceptLanguage ?? '(none)'} and answered ${read.status}, ` +
58
+ `${describeBody(read.body, text)}; the table's first row reads ` +
59
+ `${firstRow ? `'${firstRow}'` : '(no rows)'}`,
60
+ { cause: error },
61
+ );
62
+ }
63
+ }
64
+
65
+ function describeBody(body, text) {
66
+ if (body === null) return 'its body unavailable';
67
+ return `${body.includes(text) ? 'WITH' : 'WITHOUT'} '${text}' in its body`;
68
+ }
package/test/form.js CHANGED
@@ -122,9 +122,13 @@ export async function fillForm(page, manifest, entity, record, relationSamples,
122
122
  if (record[field.name] === undefined) continue;
123
123
  await fillField(page, field, record[field.name], opts);
124
124
  }
125
+ // a readOnly relation renders as a disabled combobox (an update-time calculated action, a
126
+ // platform-owned FK) - the server sets it, so there is nothing to pick (dirigible #7554); the
127
+ // REST flows still post its sample, which the calculated action overwrites or checks
128
+ const pickable = relationSamples.filter((s) => !s.relation.readOnly);
125
129
  // cascade order: a dependsOn trigger must be picked BEFORE its dependent, so the narrowed
126
130
  // option list is the one the dependent's sample was chosen from
127
- const triggers = relationSamples.filter((s) => !s.relation.dependsOn);
128
- const dependents = relationSamples.filter((s) => s.relation.dependsOn);
131
+ const triggers = pickable.filter((s) => !s.relation.dependsOn);
132
+ const dependents = pickable.filter((s) => s.relation.dependsOn);
129
133
  for (const sample of [...triggers, ...dependents]) await pickDropdown(page, sample.relation, sample.label);
130
134
  }